From 5806eda4955a9df8fd04531f82521545ab29d748 Mon Sep 17 00:00:00 2001 From: Mads Sandberg Brun Date: Thu, 14 Jul 2022 08:31:24 +0200 Subject: [PATCH 01/51] Set executor of CompletableFuture for response with no content. --- .../main/java/io/helidon/jersey/connector/HelidonConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2eca2717799..c22b23ba5bd 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 @@ -203,7 +203,7 @@ public String getReasonPhrase() { final CompletionStage stream = HelidonStructures.hasEntity(webClientResponse) ? webClientResponse.content().as(InputStream.class) - : CompletableFuture.supplyAsync(() -> NO_CONTENT_INPUT_STREAM); + : CompletableFuture.supplyAsync(() -> NO_CONTENT_INPUT_STREAM, executorServiceKeeper.getExecutorService(requestContext)); return stream.thenApply((a) -> { responseContext.setEntityStream(new FilterInputStream(a) { From 7043aaa69d0a63c604569d051bb8d24cd9377970 Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Thu, 14 Jul 2022 16:25:30 -0700 Subject: [PATCH 02/51] Fix copyright year --- .../main/java/io/helidon/jersey/connector/HelidonConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c22b23ba5bd..3e570b2b3b3 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. From af51105db70761e7b729b28a164d32e5a3bc6a81 Mon Sep 17 00:00:00 2001 From: Mads Sandberg Brun Date: Fri, 15 Jul 2022 08:52:57 +0200 Subject: [PATCH 03/51] Fix checkstyle --- .../java/io/helidon/jersey/connector/HelidonConnector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 3e570b2b3b3..6ebf8dcd980 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 @@ -203,7 +203,8 @@ public String getReasonPhrase() { final CompletionStage stream = HelidonStructures.hasEntity(webClientResponse) ? webClientResponse.content().as(InputStream.class) - : CompletableFuture.supplyAsync(() -> NO_CONTENT_INPUT_STREAM, executorServiceKeeper.getExecutorService(requestContext)); + : CompletableFuture.supplyAsync(() -> NO_CONTENT_INPUT_STREAM, + executorServiceKeeper.getExecutorService(requestContext)); return stream.thenApply((a) -> { responseContext.setEntityStream(new FilterInputStream(a) { From 468ac8ed018c7f1af528c93948a8e8c816154a26 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Wed, 13 Jul 2022 15:15:12 +0200 Subject: [PATCH 04/51] Add OpenTelemetry support (#4518) * Jaeger open telemetry client * Removed Jaeger metrics * Support for MP Open tracing on top of Open Telemetry Signed-off-by: Tomas Langer --- bom/pom.xml | 6 - dependencies/pom.xml | 18 +- metrics/jaeger/pom.xml | 71 --- .../jaeger/HelidonJaegerMetricsFactory.java | 156 ------ .../helidon/metrics/jaeger/package-info.java | 19 - metrics/jaeger/src/main/java/module-info.java | 27 - .../metrics/jaeger/HelloWorldResource.java | 36 -- .../metrics/jaeger/TestJaegerMetrics.java | 66 --- .../META-INF/microprofile-config.properties | 19 - metrics/pom.xml | 1 - .../security/SecurityCdiExtension.java | 2 +- microprofile/tracing/pom.xml | 8 + .../tracing/TracingCdiExtension.java | 38 +- .../tracing/src/main/java/module-info.java | 2 + .../microprofile/tracing/TracingTest.java | 5 +- .../integration/common/SecurityTracing.java | 6 +- .../jersey/client/ClientSecurityFilter.java | 8 +- .../io/helidon/security/SecurityImpl.java | 9 +- .../integration/nativeimage/mp1/Mp1Main.java | 19 +- .../META-INF/microprofile-config.properties | 2 + tracing/jaeger/pom.xml | 33 +- .../jaeger/JaegerDataPropagationProvider.java | 22 +- .../tracing/jaeger/JaegerScopeManager.java | 82 --- .../tracing/jaeger/JaegerTracerBuilder.java | 519 ++++++------------ .../tracing/jaeger/JaegerTracerProvider.java | 32 +- tracing/jaeger/src/main/java/module-info.java | 22 +- .../JaegerDataPropagationProviderTest.java | 104 +--- .../jaeger/JaegerScopeManagerTest.java | 88 --- .../jaeger/JaegerTracerBuilderTest.java | 108 +--- .../src/test/resources/application.yaml | 17 +- .../jersey/client/ClientTracingFilter.java | 37 +- .../client/ClientTracingInterceptor.java | 12 +- tracing/opentelemetry/pom.xml | 4 + .../opentelemetry/HelidonOpenTelemetry.java | 9 +- .../opentelemetry/OpenTelemetryScope.java | 12 +- .../opentelemetry/OpenTelemetrySpan.java | 3 +- .../OpenTelemetrySpanBuilder.java | 11 +- .../OpenTelemetrySpanContext.java | 24 +- .../opentelemetry/OpenTelemetryTracer.java | 27 +- .../OpenTelemetryTracerProvider.java | 38 +- .../src/main/java/module-info.java | 8 +- .../tracing/opentracing/OpenTracing.java | 1 + .../opentracing/OpenTracingContext.java | 8 + .../OpenTracingProviderHelper.java | 4 + .../tracing/opentracing/OpenTracingScope.java | 18 +- .../OpenTracingTracerProvider.java | 8 + .../tests/it1/OpentraceableClientE2ETest.java | 97 ++-- .../java/io/helidon/tracing/NoOpBuilder.java | 3 +- .../java/io/helidon/tracing/NoOpTracer.java | 21 +- .../helidon/tracing/NoOpTracerProvider.java | 5 + .../main/java/io/helidon/tracing/Scope.java | 8 + .../java/io/helidon/tracing/SpanContext.java | 7 + .../main/java/io/helidon/tracing/Tracer.java | 2 +- .../helidon/tracing/TracerProviderHelper.java | 17 +- .../helidon/tracing/spi/TracerProvider.java | 8 + .../io/helidon/tracing/TracerBuilderTest.java | 2 +- 56 files changed, 616 insertions(+), 1323 deletions(-) delete mode 100644 metrics/jaeger/pom.xml delete mode 100644 metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/HelidonJaegerMetricsFactory.java delete mode 100644 metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/package-info.java delete mode 100644 metrics/jaeger/src/main/java/module-info.java delete mode 100644 metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/HelloWorldResource.java delete mode 100644 metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/TestJaegerMetrics.java delete mode 100644 metrics/jaeger/src/test/resources/META-INF/microprofile-config.properties delete mode 100644 tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerScopeManager.java delete mode 100644 tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerScopeManagerTest.java diff --git a/bom/pom.xml b/bom/pom.xml index 5f60c011f0f..78983a1c576 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -727,12 +727,6 @@ helidon-tracing-jaeger ${helidon.version} - - - io.helidon.metrics - helidon-metrics-jaeger - ${helidon.version} - io.helidon.tracing diff --git a/dependencies/pom.xml b/dependencies/pom.xml index e004029ae78..fa61e11b73d 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -69,7 +69,6 @@ 1.5.18 2.13.2.20220328 - 1.6.0 2.0.1 2.0.0 3.0.0 @@ -136,6 +135,7 @@ 1.17.5 1.15.0 1.15.0-alpha + 1.15.0-alpha 0.33.0 0.2.1 0.1.8 @@ -237,22 +237,16 @@ - - io.jaegertracing - jaeger-client - ${version.lib.jaegertracing} - - - javax.annotation - javax.annotation-api - - - io.opentelemetry opentelemetry-semconv ${version.lib.opentelemetry.semconv} + + io.opentelemetry + opentelemetry-opentracing-shim + ${version.lib.opentelemetry.opentracing.shim} + io.opentracing opentracing-util diff --git a/metrics/jaeger/pom.xml b/metrics/jaeger/pom.xml deleted file mode 100644 index 62add7314c7..00000000000 --- a/metrics/jaeger/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - 4.0.0 - - io.helidon.metrics - helidon-metrics-project - 3.0.0-SNAPSHOT - - - helidon-metrics-jaeger - Helidon Metrics Jaeger - - - - io.helidon.metrics - helidon-metrics-api - - - io.jaegertracing - jaeger-core - ${version.lib.jaegertracing} - - - io.helidon.metrics - helidon-metrics - test - - - io.helidon.microprofile.bundles - helidon-microprofile - test - - - io.helidon.microprofile.tests - helidon-microprofile-tests-junit5 - test - - - io.helidon.tracing - helidon-tracing-jaeger - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.hamcrest - hamcrest-all - test - - - \ No newline at end of file diff --git a/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/HelidonJaegerMetricsFactory.java b/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/HelidonJaegerMetricsFactory.java deleted file mode 100644 index 3e7913646a1..00000000000 --- a/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/HelidonJaegerMetricsFactory.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ -package io.helidon.metrics.jaeger; - -import java.time.Duration; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.BiFunction; - -import io.helidon.common.LazyValue; -import io.helidon.metrics.api.RegistryFactory; - -import io.jaegertracing.internal.metrics.Counter; -import io.jaegertracing.internal.metrics.Gauge; -import io.jaegertracing.internal.metrics.Timer; -import io.jaegertracing.spi.MetricsFactory; -import org.eclipse.microprofile.metrics.Metadata; -import org.eclipse.microprofile.metrics.Metric; -import org.eclipse.microprofile.metrics.MetricRegistry; -import org.eclipse.microprofile.metrics.MetricType; -import org.eclipse.microprofile.metrics.MetricUnits; -import org.eclipse.microprofile.metrics.Tag; - -/** - * Exposes Jaeger tracing metrics as Helidon vendor metrics. - */ -public class HelidonJaegerMetricsFactory implements MetricsFactory { - - private final LazyValue vendorRegistry = LazyValue.create(() -> RegistryFactory.getInstance() - .getRegistry(MetricRegistry.Type.VENDOR)); - - @Override - public Counter createCounter(String name, Map jaegerTags) { - return new Counter() { - - private final org.eclipse.microprofile.metrics.Counter counter = createMetric( - name, - jaegerTags, - MetricType.COUNTER, - MetricUnits.NONE, - HelidonJaegerMetricsFactory.this::counter); - - @Override - public void inc(long delta) { - counter.inc(delta); - } - }; - } - - @Override - public Timer createTimer(String name, Map jaegerTags) { - return new Timer() { - - private final org.eclipse.microprofile.metrics.Timer timer = createMetric( - name, - jaegerTags, - MetricType.TIMER, - MetricUnits.MICROSECONDS, - HelidonJaegerMetricsFactory.this::timer); - - @Override - public void durationMicros(long time) { - timer.update(Duration.ofNanos(TimeUnit.MICROSECONDS.toNanos(time))); - } - }; - } - - @Override - public Gauge createGauge(String name, Map jaegerTags) { - return new Gauge() { - - private final JaegerGauge gauge = new JaegerGauge(); - - { - Metadata metadata = metadata(name, MetricType.GAUGE, MetricUnits.NONE); - vendorRegistry.get().register(metadata, - gauge, - convertTags(jaegerTags)); - } - - @Override - public void update(long amount) { - gauge.update(amount); - } - }; - } - - private org.eclipse.microprofile.metrics.Counter counter(Metadata metadata, Tag[] tags) { - return vendorRegistry.get().counter(metadata, tags); - } - - private org.eclipse.microprofile.metrics.Timer timer(Metadata metadata, Tag[] tags) { - return vendorRegistry.get().timer(metadata, tags); - } - - private static class JaegerGauge implements org.eclipse.microprofile.metrics.Gauge { - - private static final Long DEFAULT_VALUE = 0L; - - private Long value = DEFAULT_VALUE; - - void update(Long value) { - this.value = value; - } - - @Override - public Long getValue() { - return value; - } - } - - private static T createMetric( - String name, - Map jaegerTags, - MetricType metricType, - String metricUnits, - BiFunction metricFactoryFn) { - - Metadata metadata = metadata(name, metricType, metricUnits); - return metricFactoryFn.apply(metadata, convertTags(jaegerTags)); - } - - private static Metadata metadata(String name, MetricType metricType, String metricUnits) { - return Metadata.builder() - .withName(name) - .withDisplayName("Jaeger tracing " + name) - .withDescription("Jaeger tracing " + metricType.toString() + " for " + name) - .withType(metricType) - .withUnit(metricUnits) - .build(); - } - - static Tag[] convertTags(Map jaegerTags) { - if (jaegerTags == null) { - return new Tag[0]; - } - return jaegerTags - .entrySet() - .stream() - .map(e -> new Tag(e.getKey(), e.getValue())) - .toArray(Tag[]::new); - } -} diff --git a/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/package-info.java b/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/package-info.java deleted file mode 100644 index c824ee46162..00000000000 --- a/metrics/jaeger/src/main/java/io/helidon/metrics/jaeger/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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. - */ -/** - * Exposes Jaeger tracing metrics as Helidon vendor metrics. - */ -package io.helidon.metrics.jaeger; diff --git a/metrics/jaeger/src/main/java/module-info.java b/metrics/jaeger/src/main/java/module-info.java deleted file mode 100644 index 5c8b59cf131..00000000000 --- a/metrics/jaeger/src/main/java/module-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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. - */ -/** - * Helidon Jaeger metrics integration. - */ -module io.helidon.metrics.jaeger { - - requires java.logging; - requires io.helidon.common; - requires io.helidon.metrics.api; - requires jaeger.core; - - provides io.jaegertracing.spi.MetricsFactory with io.helidon.metrics.jaeger.HelidonJaegerMetricsFactory; -} diff --git a/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/HelloWorldResource.java b/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/HelloWorldResource.java deleted file mode 100644 index c273447522f..00000000000 --- a/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/HelloWorldResource.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ -package io.helidon.metrics.jaeger; - -import jakarta.enterprise.context.RequestScoped; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.microprofile.opentracing.Traced; - -@Traced -@RequestScoped -@Path("/helloworld") -public class HelloWorldResource { - - @GET - @Path("/hi") - @Produces(MediaType.TEXT_PLAIN) - public String hello() { - return "Hello, World!"; - } -} diff --git a/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/TestJaegerMetrics.java b/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/TestJaegerMetrics.java deleted file mode 100644 index 3e198a01ba6..00000000000 --- a/metrics/jaeger/src/test/java/io/helidon/metrics/jaeger/TestJaegerMetrics.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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. - */ -package io.helidon.metrics.jaeger; - -import java.util.Map; - -import io.helidon.microprofile.tests.junit5.AddBean; -import io.helidon.microprofile.tests.junit5.HelidonTest; - -import jakarta.inject.Inject; -import jakarta.ws.rs.client.WebTarget; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.microprofile.metrics.Counter; -import org.eclipse.microprofile.metrics.Gauge; -import org.eclipse.microprofile.metrics.MetricID; -import org.eclipse.microprofile.metrics.MetricRegistry; -import org.eclipse.microprofile.metrics.annotation.RegistryType; -import org.junit.jupiter.api.Test; - -import static io.helidon.metrics.jaeger.HelidonJaegerMetricsFactory.convertTags; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; - -@HelidonTest -@AddBean(HelloWorldResource.class) -class TestJaegerMetrics { - - @Inject - private WebTarget webTarget; - - @Inject - @RegistryType(type = MetricRegistry.Type.VENDOR) - private MetricRegistry vendorRegistry; - - @Test - void checkForJaegerMetrics() { - String hello = webTarget - .path("/helloworld/hi") - .request(MediaType.TEXT_PLAIN) - .get(String.class); - - Map gauges = vendorRegistry.getGauges(); - - MetricID expectedCounterID = new MetricID("jaeger_tracer_traces", - convertTags(Map.of("sampled", "n", "state", "started"))); - Map counters = vendorRegistry.getCounters((metricID, metric) -> metricID.equals(expectedCounterID)); - Counter expectedCounter = counters.get(expectedCounterID); - assertThat("jaeger_tracer_traces counter", expectedCounter, is(notNullValue())); - assertThat("jaeger_tracer_traces counter", expectedCounter.getCount(), is(greaterThan(0L))); - } -} diff --git a/metrics/jaeger/src/test/resources/META-INF/microprofile-config.properties b/metrics/jaeger/src/test/resources/META-INF/microprofile-config.properties deleted file mode 100644 index 0ca8c1ac916..00000000000 --- a/metrics/jaeger/src/test/resources/META-INF/microprofile-config.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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. -# -tracing.service=mp -tracing.sampler-type=const -# never sample, so test works even if Jaeger is running locally -tracing.sampler-param=0 diff --git a/metrics/pom.xml b/metrics/pom.xml index bea60901ce4..80e18aa34b1 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -33,7 +33,6 @@ metrics prometheus trace-exemplar - jaeger api service-api diff --git a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java index c61e06e3b5f..ffa6623a3ad 100644 --- a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java +++ b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityCdiExtension.java @@ -65,7 +65,7 @@ private void registerBean(@Observes BeforeBeanDiscovery abd) { // priority is high, so we update builder from config as soon as possible // all additions by other extension will override configuration options - private void configure(@Observes @RuntimeStart @Priority(PLATFORM_BEFORE) Config config) { + private void configure(@Observes @RuntimeStart @Priority(PLATFORM_BEFORE + 2) Config config) { this.config = config; securityBuilder.config(config.get("security")); } diff --git a/microprofile/tracing/pom.xml b/microprofile/tracing/pom.xml index 343eeb8ed40..5596596bbd7 100644 --- a/microprofile/tracing/pom.xml +++ b/microprofile/tracing/pom.xml @@ -44,6 +44,14 @@ io.helidon.tracing helidon-tracing-opentracing + + io.helidon.tracing + helidon-tracing-opentelemetry + + + io.opentelemetry + opentelemetry-opentracing-shim + io.helidon.tracing helidon-tracing-jersey diff --git a/microprofile/tracing/src/main/java/io/helidon/microprofile/tracing/TracingCdiExtension.java b/microprofile/tracing/src/main/java/io/helidon/microprofile/tracing/TracingCdiExtension.java index b4c880a859b..cfdfdc53f53 100644 --- a/microprofile/tracing/src/main/java/io/helidon/microprofile/tracing/TracingCdiExtension.java +++ b/microprofile/tracing/src/main/java/io/helidon/microprofile/tracing/TracingCdiExtension.java @@ -26,7 +26,9 @@ import io.helidon.tracing.TracerBuilder; import io.helidon.webserver.WebTracingConfig; +import io.opentelemetry.opentracingshim.OpenTracingShim; import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.Initialized; @@ -53,7 +55,8 @@ private void observeBeforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) { bbd.addAnnotatedType(TracerProducer.class, "TracingTracerProducer"); } - private void prepareTracer(@Observes @Priority(PLATFORM_BEFORE + 11) @Initialized(ApplicationScoped.class) Object event, + // must be configured before security + private void prepareTracer(@Observes @Priority(PLATFORM_BEFORE + 1) @Initialized(ApplicationScoped.class) Object event, BeanManager bm) { JaxRsCdiExtension jaxrs = bm.getExtension(JaxRsCdiExtension.class); ServerCdiExtension server = bm.getExtension(ServerCdiExtension.class); @@ -70,13 +73,32 @@ private void prepareTracer(@Observes @Priority(PLATFORM_BEFORE + 11) @Initialize .config(config) .build(); - Tracer tracer; + if (!helidonTracer.enabled()) { + Logger.getLogger(TracingCdiExtension.class.getName()) + .warning("helidon-microprofile-tracing is on the classpath, yet there is no tracer implementation " + + "library. Tracing uses a no-op tracer. As a result, no tracing will be configured" + + " for WebServer and JAX-RS"); + + Contexts.globalContext().register(io.helidon.tracing.Tracer.noOp()); + Contexts.globalContext().register(GlobalTracer.get()); + // no need to register all of this + return; + } + + Tracer registeredTracer; try { - tracer = helidonTracer.unwrap(Tracer.class); + registeredTracer = helidonTracer.unwrap(Tracer.class); } catch (Exception e) { - throw new DeploymentException("MicroProfile tracing requires an OpenTracing based tracer", e); + try { + io.opentelemetry.api.trace.Tracer otelTracer = helidonTracer.unwrap(io.opentelemetry.api.trace.Tracer.class); + registeredTracer = OpenTracingShim.createTracerShim(otelTracer); + } catch (Exception ex) { + throw new DeploymentException("MicroProfile tracing requires an OpenTracing or OpenTelemetry based tracer", ex); + } } + Tracer tracer = registeredTracer; + // tracer is available in global Contexts.globalContext().register(tracer); Contexts.globalContext().register(helidonTracer); @@ -87,15 +109,7 @@ private void prepareTracer(@Observes @Priority(PLATFORM_BEFORE + 11) @Initialize Contexts.context() .ifPresent(ctx -> ctx.register(tracer)); - if (!helidonTracer.enabled()) { - Logger.getLogger(TracingCdiExtension.class.getName()) - .warning("helidon-microprofile-tracing is on the classpath, yet there is no tracer implementation " - + "library. Tracing uses a no-op tracer. As a result, no tracing will be configured" - + " for WebServer and JAX-RS"); - // no need to register all of this - return; - } server.serverRoutingBuilder() .register(WebTracingConfig.create(config)); diff --git a/microprofile/tracing/src/main/java/module-info.java b/microprofile/tracing/src/main/java/module-info.java index 71d375b0f1d..5ed3b67e610 100644 --- a/microprofile/tracing/src/main/java/module-info.java +++ b/microprofile/tracing/src/main/java/module-info.java @@ -39,10 +39,12 @@ requires transitive io.helidon.tracing; requires transitive io.helidon.tracing.jersey; requires io.helidon.tracing.tracerresolver; + requires io.helidon.tracing.opentelemetry; requires transitive microprofile.opentracing.api; requires microprofile.rest.client.api; requires io.opentracing.util; + requires io.opentelemetry.opentracingshim; exports io.helidon.microprofile.tracing; diff --git a/microprofile/tracing/src/test/java/io/helidon/microprofile/tracing/TracingTest.java b/microprofile/tracing/src/test/java/io/helidon/microprofile/tracing/TracingTest.java index b13e4d725b8..4929337d13b 100644 --- a/microprofile/tracing/src/test/java/io/helidon/microprofile/tracing/TracingTest.java +++ b/microprofile/tracing/src/test/java/io/helidon/microprofile/tracing/TracingTest.java @@ -38,6 +38,7 @@ import static io.helidon.tracing.jersey.client.ClientTracingFilter.X_OT_SPAN_CONTEXT; import static io.helidon.tracing.jersey.client.ClientTracingFilter.X_REQUEST_ID; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -89,14 +90,14 @@ void testTracingPropagation() { // make sure that the operation is as expected (e.g. correctly propagated) String headerValue = (String) response.getHeaders().getFirst("X-FRONT-X-TEST-TRACER-OPERATION"); - assertThat(headerValue, is("GET")); + assertThat(headerValue, startsWith("GET")); headerValue = (String) response.getHeaders().getFirst("X-FRONT-" + X_REQUEST_ID); assertThat(headerValue, is(xRequestId)); headerValue = (String) response.getHeaders().getFirst("X-FRONT-" + X_OT_SPAN_CONTEXT); assertThat(headerValue, is(xOtSpanContext)); headerValue = (String) response.getHeaders().getFirst("X-HELLO-X-TEST-TRACER-OPERATION"); - assertThat(headerValue, is("GET")); + assertThat(headerValue, startsWith("GET")); headerValue = (String) response.getHeaders().getFirst("X-HELLO-" + X_REQUEST_ID); assertThat(headerValue, is(xRequestId)); headerValue = (String) response.getHeaders().getFirst("X-HELLO-" + X_OT_SPAN_CONTEXT); diff --git a/security/integration/common/src/main/java/io/helidon/security/integration/common/SecurityTracing.java b/security/integration/common/src/main/java/io/helidon/security/integration/common/SecurityTracing.java index 3bd2c45564e..d84fb747c05 100644 --- a/security/integration/common/src/main/java/io/helidon/security/integration/common/SecurityTracing.java +++ b/security/integration/common/src/main/java/io/helidon/security/integration/common/SecurityTracing.java @@ -239,8 +239,10 @@ public AtzTracing atzTracing() { public OutboundTracing outboundTracing() { // outbound tracing should be based on current outbound span - Optional parentOptional = Contexts.context() - .flatMap(ctx -> ctx.get(TracingConfigUtil.OUTBOUND_SPAN_QUALIFIER, SpanContext.class)); + Optional parentOptional = Span.current() + .map(Span::context) + .or(() -> Contexts.context() + .flatMap(ctx -> ctx.get(TracingConfigUtil.OUTBOUND_SPAN_QUALIFIER, SpanContext.class))); if (!parentOptional.isPresent()) { parentOptional = parentSpanContext(); } diff --git a/security/integration/jersey-client/src/main/java/io/helidon/security/integration/jersey/client/ClientSecurityFilter.java b/security/integration/jersey-client/src/main/java/io/helidon/security/integration/jersey/client/ClientSecurityFilter.java index 5eb0a79538d..e853d9ed70b 100644 --- a/security/integration/jersey-client/src/main/java/io/helidon/security/integration/jersey/client/ClientSecurityFilter.java +++ b/security/integration/jersey-client/src/main/java/io/helidon/security/integration/jersey/client/ClientSecurityFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. + * Copyright (c) 2019, 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. @@ -109,7 +109,7 @@ private void doFilter(ClientRequestContext requestContext) { private void outboundSecurity(ClientRequestContext requestContext, SecurityContext securityContext) { OutboundTracing tracing = SecurityTracing.get().outboundTracing(); - Optional explicityProvider = property(requestContext, String.class, ClientSecurity.PROPERTY_PROVIDER); + Optional explicitProvider = property(requestContext, String.class, ClientSecurity.PROPERTY_PROVIDER); try { SecurityEnvironment.Builder outboundEnv = securityContext.env() @@ -133,10 +133,10 @@ private void outboundSecurity(ClientRequestContext requestContext, SecurityConte OutboundSecurityClientBuilder clientBuilder = securityContext.outboundClientBuilder() .outboundEnvironment(outboundEnv) - .tracingSpan(tracing.findParent().orElse(null)) + .update(it -> tracing.findParent().ifPresent(it::tracingSpan)) .outboundEndpointConfig(outboundEp); - explicityProvider.ifPresent(clientBuilder::explicitProvider); + explicitProvider.ifPresent(clientBuilder::explicitProvider); OutboundSecurityResponse providerResponse = clientBuilder.buildAndGet(); SecurityResponse.SecurityStatus status = providerResponse.status(); 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 ce513340709..1ec3c109a20 100644 --- a/security/security/src/main/java/io/helidon/security/SecurityImpl.java +++ b/security/security/src/main/java/io/helidon/security/SecurityImpl.java @@ -29,6 +29,7 @@ import java.util.function.Supplier; import java.util.logging.Logger; +import io.helidon.common.LazyValue; import io.helidon.common.reactive.Single; import io.helidon.config.Config; import io.helidon.security.internal.SecurityAuditEvent; @@ -67,7 +68,7 @@ final class SecurityImpl implements Security { private final Optional subjectMappingProvider; private final String instanceUuid; private final ProviderSelectionPolicy providerSelectionPolicy; - private final Tracer securityTracer; + private final LazyValue securityTracer; private final SecurityTime serverTime; private final Supplier executorService; private final Config securityConfig; @@ -84,7 +85,7 @@ final class SecurityImpl implements Security { this.serverTime = builder.serverTime(); this.executorService = builder.executorService(); this.annotations.addAll(SecurityUtil.getAnnotations(builder.allProviders())); - this.securityTracer = SecurityUtil.getTracer(builder.tracingEnabled(), builder.tracer()); + this.securityTracer = LazyValue.create(() -> SecurityUtil.getTracer(builder.tracingEnabled(), builder.tracer())); this.subjectMappingProvider = Optional.ofNullable(builder.subjectMappingProvider()); this.securityConfig = builder.config(); @@ -173,7 +174,7 @@ public SecurityContext.Builder contextBuilder(String id) { return new SecurityContext.Builder(this) .id(newId) .executorService(executorService) - .tracingTracer(securityTracer) + .tracingTracer(securityTracer.get()) .serverTime(serverTime); } @@ -184,7 +185,7 @@ public SecurityContext createContext(String id) { @Override public Tracer tracer() { - return securityTracer; + return securityTracer.get(); } @Override diff --git a/tests/integration/native-image/mp-1/src/main/java/io/helidon/tests/integration/nativeimage/mp1/Mp1Main.java b/tests/integration/native-image/mp-1/src/main/java/io/helidon/tests/integration/nativeimage/mp1/Mp1Main.java index c0cc9d2adcc..979d6ac55d4 100644 --- a/tests/integration/native-image/mp-1/src/main/java/io/helidon/tests/integration/nativeimage/mp1/Mp1Main.java +++ b/tests/integration/native-image/mp-1/src/main/java/io/helidon/tests/integration/nativeimage/mp1/Mp1Main.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Base64; @@ -39,9 +40,8 @@ import io.helidon.security.jwt.SignedJwt; import io.helidon.security.jwt.jwk.JwkKeys; import io.helidon.security.jwt.jwk.JwkRSA; +import io.helidon.tracing.Tracer; -import io.opentracing.Tracer; -import io.opentracing.util.GlobalTracer; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.CDI; import jakarta.json.JsonArray; @@ -70,6 +70,7 @@ private Mp1Main() { /** * Application main entry point. + * * @param args command line arguments. */ public static void main(final String[] args) { @@ -124,6 +125,13 @@ public static void main(final String[] args) { if (failed) { System.exit(-1); } + + // let the tracer finish + try { + Thread.sleep(Duration.ofSeconds(2).toMillis()); + } catch (InterruptedException ignored) { + } + System.exit(0); } private static String generateJwtToken() { @@ -371,10 +379,11 @@ private static void validateJwtProtectedResource(Errors.Collector collector, Web } private static void validateTracing(Errors.Collector collector) { - Tracer tracer = GlobalTracer.get(); - if (!tracer.toString().contains("Jaeger")) { + Tracer tracer = Tracer.global(); + if (!tracer.toString().contains("OpenTelemetry")) { // could not find how to get the actual instance of tracer from API - collector.fatal(tracer, "This application should be configured with Jaeger tracer, yet it is not: " + tracer); + collector.fatal(tracer, + "This application should be configured with OpenTelemetry Jaeger tracer, yet it is not: " + tracer); } } diff --git a/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties b/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties index bcacb0f79ea..56117ce7648 100644 --- a/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties +++ b/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties @@ -22,6 +22,8 @@ app.jaxrs.number=142 mp.jwt.verify.publickey.location=/verify-jwk.json mp.jwt.verify.issuer=native-image-mp1 +tracing.service=mp1 + features.print-details=true server.static.classpath.context=/static diff --git a/tracing/jaeger/pom.xml b/tracing/jaeger/pom.xml index 27015c7d5af..66d10e0eed2 100644 --- a/tracing/jaeger/pom.xml +++ b/tracing/jaeger/pom.xml @@ -34,22 +34,20 @@ - io.jaegertracing - jaeger-client - - - - org.apache.tomcat.embed - tomcat-embed-core - - + io.helidon.common + helidon-common-configurable + + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-exporter-jaeger io.helidon.tracing - helidon-tracing-opentracing + helidon-tracing-opentelemetry io.helidon.common @@ -67,10 +65,6 @@ io.helidon.tracing helidon-tracing - - io.opentracing - opentracing-util - org.slf4j @@ -106,11 +100,6 @@ mockito-core test - - io.opentracing - opentracing-mock - test - io.helidon.config helidon-config-yaml diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerDataPropagationProvider.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerDataPropagationProvider.java index ea820e11eed..d4f832de0f8 100644 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerDataPropagationProvider.java +++ b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerDataPropagationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,17 +18,17 @@ import io.helidon.common.context.Contexts; import io.helidon.common.context.spi.DataPropagationProvider; - -import io.opentracing.Scope; -import io.opentracing.Span; -import io.opentracing.Tracer; -import io.opentracing.util.GlobalTracer; +import io.helidon.tracing.Scope; +import io.helidon.tracing.Span; +import io.helidon.tracing.Tracer; +import io.helidon.tracing.opentelemetry.OpenTelemetryTracerProvider; /** * A data propagation provider for Jaeger. Makes sure span are properly propagated * across threads managed by {@link io.helidon.common.context.ContextAwareExecutorService}. */ public class JaegerDataPropagationProvider implements DataPropagationProvider { + private static final System.Logger LOGGER = System.getLogger(JaegerDataPropagationProvider.class.getName()); static class JaegerContext { private final Span span; @@ -54,7 +54,7 @@ Scope scope() { @Override public JaegerContext data() { return Contexts.context().map(context -> context.get(Span.class).map(span -> { - Tracer tracer = context.get(Tracer.class).orElseGet(GlobalTracer::get); + Tracer tracer = context.get(Tracer.class).orElseGet(OpenTelemetryTracerProvider::globalTracer); return new JaegerContext(tracer, span); }).orElse(null)).orElse(null); } @@ -67,7 +67,7 @@ public JaegerContext data() { @Override public void propagateData(JaegerContext context) { if (context != null) { - context.scope = context.tracer.scopeManager().activate(context.span); + context.scope = Span.current().map(Span::activate).orElse(null); } } @@ -77,7 +77,11 @@ public void propagateData(JaegerContext context) { @Override public void clearData(JaegerContext context) { if (context != null && context.scope != null) { - context.scope.close(); + try { + context.scope.close(); + } catch (Exception e) { + LOGGER.log(System.Logger.Level.TRACE, "Cannot close tracing span", e); + } } } } diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerScopeManager.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerScopeManager.java deleted file mode 100644 index 6d450d3d6df..00000000000 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerScopeManager.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - */ - -package io.helidon.tracing.jaeger; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import io.opentracing.Scope; -import io.opentracing.ScopeManager; -import io.opentracing.Span; - -/** - * An implementation of {@link ScopeManager} that closes a scope from any thread. - * This is necessary to support async calls where the close operation may be - * called from a different thread. - */ -class JaegerScopeManager implements ScopeManager { - - private static final ConcurrentMap SCOPES = new ConcurrentHashMap<>(); - - @Override - public Scope activate(Span span) { - return new ThreadScope(span); - } - - @Override - public Span activeSpan() { - ThreadScope scope = SCOPES.get(Thread.currentThread().getId()); - return scope == null ? null : scope.span(); - } - - void clearScopes() { - SCOPES.clear(); - } - - static class ThreadScope implements Scope { - private final Span span; - private final ThreadScope previousScope; - private final long creatingThreadId; - private boolean closed; - - ThreadScope(Span span) { - this.span = span; - this.creatingThreadId = Thread.currentThread().getId(); - this.previousScope = SCOPES.put(this.creatingThreadId, this); - } - - @Override - public void close() { - if (!closed) { - if (previousScope == null) { - SCOPES.remove(this.creatingThreadId, this); - } else { - SCOPES.put(this.creatingThreadId, previousScope); - } - closed = true; - } - } - - Span span() { - return span; - } - - boolean isClosed() { - return closed; - } - } -} diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java index 966dcbafe38..bcb3b4e7b7a 100644 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java +++ b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java @@ -16,26 +16,35 @@ package io.helidon.tracing.jaeger; -import java.util.ArrayList; +import java.time.Duration; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import io.helidon.config.Config; import io.helidon.config.metadata.Configured; -import io.helidon.config.metadata.ConfiguredOption; -import io.helidon.tracing.opentracing.OpenTracingTracerBuilder; - -import io.jaegertracing.Configuration; -import io.jaegertracing.internal.JaegerTracer; -import io.opentracing.Tracer; -import io.opentracing.noop.NoopTracerFactory; -import io.opentracing.util.GlobalTracer; +import io.helidon.tracing.Tracer; +import io.helidon.tracing.TracerBuilder; +import io.helidon.tracing.opentelemetry.HelidonOpenTelemetry; +import io.helidon.tracing.opentelemetry.OpenTelemetryTracerProvider; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; +import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporterBuilder; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; /** - * The JaegerTracerBuilder is a convenience builder for {@link io.opentracing.Tracer} to use with Jaeger. + * The JaegerTracerBuilder is a convenience builder for {@link io.helidon.tracing.Tracer} to use with Jaeger. *

* Unless You want to explicitly depend on Jaeger in Your code, please * use {@link io.helidon.tracing.TracerBuilder#create(String)} or @@ -45,7 +54,7 @@ * Except for {@code protocol} and {@code service} these are honored, unless overridden in configuration * or through the builder methods. * See Jaeger documentation - * for details. + * for details. *

* The following table lists jaeger specific defaults and configuration options. * @@ -58,78 +67,58 @@ * * * - * + * * * * * - * + * * * * - * - * + * + * * * * - * - * + * + * * * * - * - * - * - * - * * - * + * * * - * - * - * + * + * + * * * - * + * * - * - * - * - * - * - * + * * * - * - * - * - * - * - * - * - * + * + * + * * * - * - * - * + * + * + * * * * - * - * + * + * * * * - * - * - * - * - * - * - * + * + * * * * @@ -147,37 +136,29 @@ * * *
{@code service} Required service nameService name
{@code protocol}{@code http}The protocol to use. By default http is used. To switch to agent - * mode, use {@code udp}The protocol to use. By default http is used.
{@code host}{@code 127.0.0.1} for {@code http}, library default for {@code udp}Host to used - used by both UDP and HTTP endpoints{@code 127.0.0.1}Host to use
{@code port}{@code 14268} for {@code http}, library default for {@code udp}Port to be used - used by both UDP and HTTP endpoints{@value #DEFAULT_HTTP_PORT}Port to be used
{@code path}{@code /api/traces}Path to be used when using {@code http}
{@code token} Authentication token to usePath to be used.
{@code username} User to use to authenticate (basic authentication){@code exporter-timeout-millis}10 secondsTimeout of exporter
{@code password}{@code private-key-pem} Password to use to authenticate (basic authentication)
{@code propagation}library defaultPropagation type to use, supports {@code jaeger} and {@code b3}Client private key in PEM format
{@code log-spans}library defaultWhether reporter should log spans
{@code max-queue-size}library defaultMaximal queue size of the reporter{@code client-cert-pem} Client certificate in PEM format
{@code flush-interval-ms}library defaultReporter flush interval in milliseconds{@code trusted-cert-pem} Trusted certificates in PEM format
{@code sampler-type}library defaultSampler type ({@code const}, {@code probabilistic}, {@code ratelimiting}, or {@code remote}){@code const} with param set to {@code 1}Sampler type {@code const} (0 to disable, 1 to always enabled), + * {@code ratio} (sample param contains the ratio as a double)
{@code sampler-param}library defaultNumeric parameter specifying details for the sampler type (see Jaeger docs)
sampler-managerlibrary defaulthost and port of the sampler managersampler type defaultNumeric parameter specifying details for the sampler type.
{@code tags}see {@link io.helidon.tracing.TracerBuilder}
- * - * @see Jaeger configuration */ @Configured(prefix = "tracing", root = true, description = "Jaeger tracer configuration.") -public class JaegerTracerBuilder implements OpenTracingTracerBuilder { +public class JaegerTracerBuilder implements TracerBuilder { static final Logger LOGGER = Logger.getLogger(JaegerTracerBuilder.class.getName()); static final boolean DEFAULT_ENABLED = true; static final String DEFAULT_HTTP_HOST = "localhost"; - static final int DEFAULT_HTTP_PORT = 14268; - static final String DEFAULT_HTTP_PATH = "/api/traces"; - + static final int DEFAULT_HTTP_PORT = 14250; private final Map tags = new HashMap<>(); - private final List propagations = new ArrayList<>(); private String serviceName; - private String protocol; - private String host; - private Integer port; - private String path; - private String token; - private String username; - private String password; - private Boolean reporterLogSpans; - private Integer reporterMaxQueueSize; - private Long reporterFlushIntervalMillis; - private SamplerType samplerType; - private Number samplerParam; - private String samplerManager; + private String protocol = "http"; + private String host = DEFAULT_HTTP_HOST; + private int port = DEFAULT_HTTP_PORT; + private SamplerType samplerType = SamplerType.CONSTANT; + private Number samplerParam = 1; private boolean enabled = DEFAULT_ENABLED; private boolean global = true; + private Duration exporterTimeout = Duration.ofSeconds(10); + private byte[] privateKey; + private byte[] certificate; + private byte[] trustedCertificates; + private String path; /** * Default constructor, does not modify any state. @@ -186,7 +167,7 @@ protected JaegerTracerBuilder() { } /** - * Get a Jaeger {@link io.opentracing.Tracer} builder for processing tracing data of a service with a given name. + * Get a Jaeger {@link io.helidon.tracing.Tracer } builder for processing tracing data of a service with a given name. * * @param serviceName name of the service that will be using the tracer. * @return {@code Tracer} builder for Jaeger. @@ -224,24 +205,12 @@ public JaegerTracerBuilder collectorProtocol(String protocol) { return this; } - @Override - public JaegerTracerBuilder collectorHost(String host) { - this.host = host; - return this; - } - @Override public JaegerTracerBuilder collectorPort(int port) { this.port = port; return this; } - /** - * Override path to use. - * - * @param path path to override the default - * @return updated builder instance - */ @Override public JaegerTracerBuilder collectorPath(String path) { this.path = path; @@ -249,8 +218,8 @@ public JaegerTracerBuilder collectorPath(String path) { } @Override - public JaegerTracerBuilder enabled(boolean enabled) { - this.enabled = enabled; + public JaegerTracerBuilder collectorHost(String host) { + this.host = host; return this; } @@ -272,40 +241,6 @@ public JaegerTracerBuilder addTracerTag(String key, boolean value) { return this; } - @Override - public JaegerTracerBuilder registerGlobal(boolean global) { - this.global = global; - return this; - } - - /** - * Configure username and password for basic authentication. - * - * @param username username to use - * @param password password to use - * @return updated builder instance - */ - @ConfiguredOption(key = "username", type = String.class) - @ConfiguredOption(key = "password", type = String.class) - public JaegerTracerBuilder basicAuth(String username, String password) { - username(username); - password(password); - return this; - } - - /** - * Add propagation type to use. - * - * @param propagation propagation value - * @return updated builder instance - */ - @ConfiguredOption(key = "propagation", kind = ConfiguredOption.Kind.LIST, type = Configuration.Propagation.class) - public JaegerTracerBuilder addPropagation(Configuration.Propagation propagation) { - this.propagations.add(propagation); - - return this; - } - @Override public JaegerTracerBuilder config(Config config) { config.get("enabled").asBoolean().ifPresent(this::enabled); @@ -314,22 +249,12 @@ public JaegerTracerBuilder config(Config config) { config.get("host").asString().ifPresent(this::collectorHost); config.get("port").asInt().ifPresent(this::collectorPort); config.get("path").asString().ifPresent(this::collectorPath); - config.get("token").asString().ifPresent(this::token); - config.get("username").asString().ifPresent(this::username); - config.get("password").asString().ifPresent(this::password); - config.get("propagation").asList(String.class).ifPresent(propagations -> { - propagations.stream() - .map(String::toUpperCase) - .map(Configuration.Propagation::valueOf) - .forEach(this::addPropagation); - - }); - config.get("log-spans").asBoolean().ifPresent(this::logSpans); - config.get("max-queue-size").asInt().ifPresent(this::maxQueueSize); - config.get("flush-interval-ms").asLong().ifPresent(this::flushIntervalMs); config.get("sampler-type").asString().as(SamplerType::create).ifPresent(this::samplerType); config.get("sampler-param").asDouble().ifPresent(this::samplerParam); - config.get("sampler-manager").asString().ifPresent(this::samplerManager); + config.get("exporter-timeout-millis").asLong().ifPresent(it -> exporterTimeout(Duration.ofMillis(it))); + config.get("private-key-pem").as(io.helidon.common.configurable.Resource::create).ifPresent(this::privateKey); + config.get("client-cert-pem").as(io.helidon.common.configurable.Resource::create).ifPresent(this::clientCertificate); + config.get("trusted-cert-pem").as(io.helidon.common.configurable.Resource::create).ifPresent(this::trustedCertificates); config.get("tags").detach() .asMap() @@ -358,88 +283,96 @@ public JaegerTracerBuilder config(Config config) { } /** - * The host name and port when using the remote controlled sampler. + * Private key in PEM format. * - * @param samplerManagerHostPort host and port of the sampler manager - * @return updated builder instance + * @param resource key resource + * @return updated builder */ - public JaegerTracerBuilder samplerManager(String samplerManagerHostPort) { - this.samplerManager = samplerManagerHostPort; + public JaegerTracerBuilder privateKey(io.helidon.common.configurable.Resource resource) { + this.privateKey = resource.bytes(); return this; } /** - * The sampler parameter (number). - * @param samplerParam parameter of the sampler - * @return updated builder instance + * Certificate of client in PEM format. + * + * @param resource certificate resource + * @return updated builder */ - public JaegerTracerBuilder samplerParam(Number samplerParam) { - this.samplerParam = samplerParam; + public JaegerTracerBuilder clientCertificate(io.helidon.common.configurable.Resource resource) { + this.certificate = resource.bytes(); return this; } /** - * Sampler type. - *

- * See Sampler types. + * Trusted certificates in PEM format. * - * @param samplerType type of the sampler - * @return updated builder instance + * @param resource trusted certificates resource + * @return updated builder */ - public JaegerTracerBuilder samplerType(SamplerType samplerType) { - this.samplerType = samplerType; + public JaegerTracerBuilder trustedCertificates(io.helidon.common.configurable.Resource resource) { + this.trustedCertificates = resource.bytes(); return this; } /** - * The reporter's flush interval. + * Timeout of exporter requests. * - * @param value amount in the unit specified - * @param timeUnit the time unit - * @return updated builder instance + * @param exporterTimeout timeout to use + * @return updated builder */ - public JaegerTracerBuilder flushInterval(long value, TimeUnit timeUnit) { - flushIntervalMs(timeUnit.toMillis(value)); + public JaegerTracerBuilder exporterTimeout(Duration exporterTimeout) { + this.exporterTimeout = exporterTimeout; return this; } - /** - * The reporter's maximum queue size. - * - * @param maxQueueSize maximal size of the queue - * @return updated builder instance - */ - public JaegerTracerBuilder maxQueueSize(int maxQueueSize) { - this.reporterMaxQueueSize = maxQueueSize; + @Override + public JaegerTracerBuilder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + @Override + public JaegerTracerBuilder registerGlobal(boolean global) { + this.global = global; return this; } + @Override + public B unwrap(Class builderClass) { + if (builderClass.isAssignableFrom(getClass())) { + return builderClass.cast(this); + } + throw new IllegalArgumentException("This builder is a Jaeger tracer builder, cannot be unwrapped to " + + builderClass.getName()); + } + /** - * Whether the reporter should also log the spans. + * The sampler parameter (number). * - * @param logSpans whether to log spans + * @param samplerParam parameter of the sampler * @return updated builder instance */ - @ConfiguredOption - public JaegerTracerBuilder logSpans(boolean logSpans) { - this.reporterLogSpans = logSpans; + public JaegerTracerBuilder samplerParam(Number samplerParam) { + this.samplerParam = samplerParam; return this; } /** - * Authentication token sent as a "Bearer" to the endpoint. + * Sampler type. + *

+ * See Sampler types. * - * @param token token to authenticate + * @param samplerType type of the sampler * @return updated builder instance */ - @ConfiguredOption - public JaegerTracerBuilder token(String token) { - this.token = token; + public JaegerTracerBuilder samplerType(SamplerType samplerType) { + this.samplerType = samplerType; return this; } /** - * Builds the {@link io.opentracing.Tracer} for Jaeger based on the configured parameters. + * Builds the {@link io.helidon.tracing.Tracer} for Jaeger based on the configured parameters. * * @return the tracer */ @@ -448,138 +381,68 @@ public Tracer build() { Tracer result; if (enabled) { - if (null == serviceName) { + if (serviceName == null) { throw new IllegalArgumentException( "Configuration must at least contain the 'service' key ('tracing.service` in MP) with service name"); } - JaegerTracer.Builder builder = jaegerConfig().getTracerBuilder(); - builder.withScopeManager(new JaegerScopeManager()); // use our scope manager - result = builder.build(); - LOGGER.info(() -> "Creating Jaeger tracer for '" + serviceName + "' configured with " + protocol + "://" - + host + ":" + port); - } else { - LOGGER.info("Jaeger Tracer is explicitly disabled."); - result = NoopTracerFactory.create(); - } - - if (global) { - GlobalTracer.registerIfAbsent(result); - } - - return result; - } - - @Override - public boolean enabled() { - return enabled; - } + JaegerGrpcSpanExporterBuilder spanExporterBuilder = JaegerGrpcSpanExporter.builder() + .setEndpoint(protocol + "://" + host + ":" + port + (path == null ? "" : path)) + .setTimeout(exporterTimeout); - @Override - public B unwrap(Class builderClass) { - if (builderClass.isAssignableFrom(getClass())) { - return builderClass.cast(this); - } - throw new IllegalArgumentException("This builder is a Jaeger tracer builder, cannot be unwrapped to " - + builderClass.getName()); - } - - Configuration jaegerConfig() { - /* - * Preload from environment, then override configured values - */ - Configuration config = Configuration.fromEnv(serviceName); - - if (null != tags) { - config.withTracerTags(tags); - } - - /* - * Sender configuration - */ - Configuration.SenderConfiguration sender = Configuration.SenderConfiguration.fromEnv(); - if ((null == protocol) || "http".equals(protocol) || "https".equals(protocol)) { - if (null == host) { - host = DEFAULT_HTTP_HOST; + if (privateKey != null && certificate != null) { + spanExporterBuilder.setClientTls(privateKey, certificate); } - if (null == port) { - port = DEFAULT_HTTP_PORT; - } - if (null == path) { - path = DEFAULT_HTTP_PATH; - } - if (null == protocol) { - protocol = "http"; - } - sender.withEndpoint(protocol + "://" + host + ":" + port + path); - } else if ("udp".equals(protocol)) { - // UDP - if (null != host) { - sender.withAgentHost(host); - } - if (null != port) { - sender.withAgentPort(port); - } - } // else use library defaults for other type of protocols - if (null != token) { - sender.withAuthToken(token); - } else { - // token has precedence over basic auth - if (null != username) { - sender.withAuthUsername(username); + if (trustedCertificates != null) { + spanExporterBuilder.setTrustedCertificates(trustedCertificates); } - if (null != password) { - sender.withAuthPassword(password); - } - } - - Configuration.ReporterConfiguration reporter = Configuration.ReporterConfiguration.fromEnv(); - if (null != reporterLogSpans) { - reporter.withLogSpans(reporterLogSpans); - } - if (null != reporterMaxQueueSize) { - reporter.withMaxQueueSize(reporterMaxQueueSize); - } - if (null != reporterFlushIntervalMillis) { - reporter.withFlushInterval(reporterFlushIntervalMillis.intValue()); - } - reporter.withSender(sender); - config.withReporter(reporter); - - if (null != samplerType) { - Configuration.SamplerConfiguration sampler = Configuration.SamplerConfiguration.fromEnv(); - sampler.withType(samplerType.config); - - if (null != samplerParam) { - sampler.withParam(samplerParam); + SpanExporter exporter = spanExporterBuilder.build(); + + Sampler sampler = switch (samplerType) { + case RATIO -> Sampler.traceIdRatioBased(samplerParam.doubleValue()); + case CONSTANT -> samplerParam.intValue() == 1 + ? Sampler.alwaysOn() + : Sampler.alwaysOff(); + }; + + Resource serviceName = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, this.serviceName)); + OpenTelemetry ot = OpenTelemetrySdk.builder() + .setTracerProvider(SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .setSampler(sampler) + .setResource(serviceName) + .build()) + .setPropagators(ContextPropagators.create(B3Propagator.injectingMultiHeaders())) + .build(); + + result = HelidonOpenTelemetry.create(ot, ot.getTracer(this.serviceName), tags); + + if (global) { + GlobalOpenTelemetry.set(ot); } - if (null != samplerManager) { - sampler.withManagerHostPort(samplerManager); - } - - config.withSampler(sampler); + LOGGER.info(() -> "Creating Jaeger tracer for '" + this.serviceName + "' configured with " + protocol + + "://" + host + ":" + port); + } else { + LOGGER.info("Jaeger Tracer is explicitly disabled."); + result = Tracer.noOp(); } - if (!propagations.isEmpty()) { - Configuration.CodecConfiguration codec = Configuration.CodecConfiguration.fromEnv(); - for (Configuration.Propagation propagation : propagations) { - codec.withPropagation(propagation); - } - config.withCodec(codec); + if (global) { + OpenTelemetryTracerProvider.globalTracer(result); } - return config; + return result; } - Map tags() { - return tags; + String path() { + return path; } - List propagations() { - return propagations; + Map tags() { + return tags; } String serviceName() { @@ -598,34 +461,6 @@ Integer port() { return port; } - String path() { - return path; - } - - String token() { - return token; - } - - String username() { - return username; - } - - String password() { - return password; - } - - Boolean reporterLogSpans() { - return reporterLogSpans; - } - - Integer reporterMaxQueueSize() { - return reporterMaxQueueSize; - } - - Long reporterFlushIntervalMillis() { - return reporterFlushIntervalMillis; - } - SamplerType samplerType() { return samplerType; } @@ -634,26 +469,10 @@ Number samplerParam() { return samplerParam; } - String samplerManager() { - return samplerManager; - } - boolean isEnabled() { return enabled; } - private void username(String username) { - this.username = username; - } - - private void password(String password) { - this.password = password; - } - - private void flushIntervalMs(Long aLong) { - this.reporterFlushIntervalMillis = aLong; - } - /** * Sampler type definition. * Available options are "const", "probabilistic", "ratelimiting" and "remote". @@ -665,31 +484,15 @@ public enum SamplerType { */ CONSTANT("const"), /** - * Probabilistic sampler makes a random sampling decision with the - * probability of sampling equal to the value of the property. - */ - PROBABILISTIC("probabilistic"), - /** - * Rate Limiting sampler uses a leaky bucket rate limiter to ensure that - * traces are sampled with a certain constant rate. + * Ratio of the requests to sample, double value. */ - RATE_LIMITING("ratelimiting"), - /** - * Remote sampler consults Jaeger agent for the appropriate sampling - * strategy to use in the current service. - */ - REMOTE("remote"); + RATIO("ratio"); private final String config; - SamplerType(String config) { this.config = config; } - String config() { - return config; - } - static SamplerType create(String value) { for (SamplerType sampler : SamplerType.values()) { if (sampler.config().equals(value)) { @@ -698,5 +501,9 @@ static SamplerType create(String value) { } throw new IllegalStateException("SamplerType " + value + " is not supported"); } + + String config() { + return config; + } } } diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerProvider.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerProvider.java index a22f7bb2b54..1398389a520 100644 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerProvider.java +++ b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerProvider.java @@ -15,18 +15,46 @@ */ package io.helidon.tracing.jaeger; +import java.util.Optional; + import io.helidon.common.Prioritized; -import io.helidon.tracing.opentracing.spi.OpenTracingProvider; +import io.helidon.tracing.Span; +import io.helidon.tracing.Tracer; +import io.helidon.tracing.opentelemetry.HelidonOpenTelemetry; +import io.helidon.tracing.opentelemetry.OpenTelemetryTracerProvider; +import io.helidon.tracing.spi.TracerProvider; +import io.opentelemetry.context.Context; import jakarta.annotation.Priority; /** * Jaeger java service. */ @Priority(Prioritized.DEFAULT_PRIORITY) -public class JaegerTracerProvider implements OpenTracingProvider { +public class JaegerTracerProvider implements TracerProvider { + @Override + public Tracer global() { + return OpenTelemetryTracerProvider.globalTracer(); + } + + @Override + public void global(Tracer tracer) { + OpenTelemetryTracerProvider.globalTracer(tracer); + } + + @Override + public Optional currentSpan() { + return Optional.ofNullable(io.opentelemetry.api.trace.Span.fromContextOrNull(Context.current())) + .map(HelidonOpenTelemetry::create); + } + @Override public JaegerTracerBuilder createBuilder() { return JaegerTracerBuilder.create(); } + + @Override + public boolean available() { + return true; + } } diff --git a/tracing/jaeger/src/main/java/module-info.java b/tracing/jaeger/src/main/java/module-info.java index 792b124b458..2201fb36eb7 100644 --- a/tracing/jaeger/src/main/java/module-info.java +++ b/tracing/jaeger/src/main/java/module-info.java @@ -18,25 +18,25 @@ * Jaeger tracing support. */ module io.helidon.tracing.jaeger { - requires io.helidon.common; - requires io.helidon.config; + requires transitive io.helidon.common; + requires transitive io.helidon.config; requires io.helidon.tracing; - requires io.helidon.tracing.opentracing; + requires io.helidon.tracing.opentelemetry; requires io.helidon.common.context; + requires io.helidon.common.configurable; + requires static io.helidon.config.metadata; requires java.logging; - requires io.opentracing.util; - requires jaeger.client; - requires jaeger.core; - requires io.opentracing.noop; - // need to explicitly require transitive dependency, as jaeger is not a module - requires com.google.gson; - requires io.opentracing.api; + + requires io.opentelemetry.sdk; + requires io.opentelemetry.exporter.jaeger; + requires io.opentelemetry.sdk.trace; + requires io.opentelemetry.sdk.common; exports io.helidon.tracing.jaeger; - provides io.helidon.tracing.opentracing.spi.OpenTracingProvider with io.helidon.tracing.jaeger.JaegerTracerProvider; + provides io.helidon.tracing.spi.TracerProvider with io.helidon.tracing.jaeger.JaegerTracerProvider; provides io.helidon.common.context.spi.DataPropagationProvider with io.helidon.tracing.jaeger.JaegerDataPropagationProvider; } diff --git a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerDataPropagationProviderTest.java b/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerDataPropagationProviderTest.java index 7583fb24c63..17fddf80131 100644 --- a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerDataPropagationProviderTest.java +++ b/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerDataPropagationProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,13 +18,10 @@ import io.helidon.common.context.Context; import io.helidon.common.context.Contexts; +import io.helidon.tracing.Scope; +import io.helidon.tracing.Span; +import io.helidon.tracing.Tracer; -import io.opentracing.Scope; -import io.opentracing.ScopeManager; -import io.opentracing.Span; -import io.opentracing.SpanContext; -import io.opentracing.Tracer; -import io.opentracing.propagation.Format; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -33,103 +30,24 @@ class JaegerDataPropagationProviderTest { private final JaegerDataPropagationProvider provider = new JaegerDataPropagationProvider(); - + @Test void dataPropagationTest() { Context context = Context.create(); - Tracer tracer = new TestTracer(); + Tracer tracer = JaegerTracerBuilder.create().serviceName("test-propagation").build(); context.register(tracer); - Span span = tracer.buildSpan("span").start(); + Span span = tracer.spanBuilder("span").start(); context.register(span); - Scope scope = tracer.scopeManager().activate(span); + Scope scope = span.activate(); context.register(scope); Contexts.runInContext(context, () -> { - assertThat(closed(scope), is(false)); + assertThat(scope.isClosed(), is(false)); JaegerDataPropagationProvider.JaegerContext data = provider.data(); provider.propagateData(data); - assertThat(closed(data.scope()), is(false)); + assertThat(data.scope().isClosed(), is(false)); provider.clearData(data); - assertThat(closed(data.scope()), is(true)); + assertThat(data.scope().isClosed(), is(true)); }); } - - private boolean closed(Scope scope) { - return ((TestScope) scope).closed; - } - - static class TestScope implements Scope { - - private final Scope delegate; - private boolean closed = false; - - TestScope(Scope delegate) { - this.delegate = delegate; - } - - @Override - public void close() { - delegate.close(); - closed = true; - } - } - - static class TestScopeManager implements ScopeManager { - - private final ScopeManager delegate; - - TestScopeManager(ScopeManager delegate) { - this.delegate = delegate; - } - - @Override - public Scope activate(Span span) { - return new TestScope(delegate.activate(span)); - } - - @Override - public Span activeSpan() { - return delegate.activeSpan(); - } - } - - static class TestTracer implements Tracer { - - private final Tracer delegate = JaegerTracerBuilder.create().serviceName("test-propagation").build(); - - @Override - public ScopeManager scopeManager() { - return new TestScopeManager(delegate.scopeManager()); - } - - @Override - public Span activeSpan() { - return delegate.activeSpan(); - } - - @Override - public Scope activateSpan(Span span) { - return delegate.activateSpan(span); - } - - @Override - public SpanBuilder buildSpan(String s) { - return delegate.buildSpan(s); - } - - @Override - public void inject(SpanContext spanContext, Format format, C c) { - delegate.inject(spanContext, format, c); - } - - @Override - public SpanContext extract(Format format, C c) { - return delegate.extract(format, c); - } - - @Override - public void close() { - delegate.close(); - } - } } diff --git a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerScopeManagerTest.java b/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerScopeManagerTest.java deleted file mode 100644 index d2553576440..00000000000 --- a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerScopeManagerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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. - */ - -package io.helidon.tracing.jaeger; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import io.jaegertracing.internal.JaegerTracer; -import io.opentracing.Span; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class JaegerScopeManagerTest { - - private final JaegerTracer tracer; - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - - JaegerScopeManagerTest() { - JaegerTracer.Builder builder = new JaegerTracer.Builder("test-scopes"); - builder.withScopeManager(new JaegerScopeManager()); - tracer = builder.build(); - } - - @BeforeEach - void clearScopes() { - JaegerScopeManager scopeManager = (JaegerScopeManager) tracer.scopeManager(); - scopeManager.clearScopes(); - } - - @Test - void testScopeManager() { - JaegerScopeManager scopeManager = (JaegerScopeManager) tracer.scopeManager(); - Span span = tracer.buildSpan("test-span").start(); - JaegerScopeManager.ThreadScope scope = (JaegerScopeManager.ThreadScope) scopeManager.activate(span); - assertFalse(scope.isClosed()); - assertEquals(scopeManager.activeSpan(), span); - scope.close(); - assertNull(scopeManager.activeSpan()); - assertTrue(scope.isClosed()); - } - - @Test - void testScopeManagerThreads() throws Exception { - JaegerScopeManager scopeManager = (JaegerScopeManager) tracer.scopeManager(); - Span span = tracer.buildSpan("test-span").start(); - JaegerScopeManager.ThreadScope scope = (JaegerScopeManager.ThreadScope) scopeManager.activate(span); - assertFalse(scope.isClosed()); - assertEquals(scopeManager.activeSpan(), span); - executor.submit(scope::close).get(100, TimeUnit.MILLISECONDS); // different thread - assertNull(scopeManager.activeSpan()); - assertTrue(scope.isClosed()); - } - - @Test - void testScopeManagerStack() { - JaegerScopeManager scopeManager = (JaegerScopeManager) tracer.scopeManager(); - Span span1 = tracer.buildSpan("test-span1").start(); - JaegerScopeManager.ThreadScope scope1 = (JaegerScopeManager.ThreadScope) scopeManager.activate(span1); - assertEquals(scopeManager.activeSpan(), span1); - Span span2 = tracer.buildSpan("test-span2").start(); - JaegerScopeManager.ThreadScope scope2 = (JaegerScopeManager.ThreadScope) scopeManager.activate(span2); - assertEquals(scopeManager.activeSpan(), span2); - scope2.close(); - assertEquals(scopeManager.activeSpan(), span1); - scope1.close(); - assertNull(scopeManager.activeSpan()); - } -} diff --git a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerTracerBuilderTest.java b/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerTracerBuilderTest.java index a61506edfcd..1f304ba53d4 100644 --- a/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerTracerBuilderTest.java +++ b/tracing/jaeger/src/test/java/io/helidon/tracing/jaeger/JaegerTracerBuilderTest.java @@ -16,26 +16,19 @@ package io.helidon.tracing.jaeger; -import java.util.List; import java.util.Map; import io.helidon.config.Config; +import io.helidon.tracing.Tracer; import io.helidon.tracing.TracerBuilder; -import io.jaegertracing.Configuration; -import io.opentracing.Tracer; -import io.opentracing.noop.NoopTracer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import static io.helidon.tracing.jaeger.JaegerTracerBuilder.DEFAULT_HTTP_HOST; -import static io.helidon.tracing.jaeger.JaegerTracerBuilder.DEFAULT_HTTP_PATH; -import static io.helidon.tracing.jaeger.JaegerTracerBuilder.DEFAULT_HTTP_PORT; -import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -67,28 +60,13 @@ void testConfigDefaults() { assertThat(jBuilder.serviceName(), is("helidon-service")); assertThat("Tags", jBuilder.tags(), is(Map.of())); - assertThat("Protocol", jBuilder.protocol(), nullValue()); - assertThat("Host", jBuilder.host(), nullValue()); - assertThat("Port", jBuilder.port(), nullValue()); + assertThat("Protocol", jBuilder.protocol(), is("http")); + assertThat("Host", jBuilder.host(), is("localhost")); + assertThat("Port", jBuilder.port(), is(14250)); assertThat("Path", jBuilder.path(), nullValue()); assertThat("Enabled", jBuilder.isEnabled(), is(true)); - assertThat("Username", jBuilder.username(), nullValue()); - assertThat("Password", jBuilder.password(), nullValue()); - assertThat("Token", jBuilder.token(), nullValue()); - assertThat("Propagations", jBuilder.propagations(), empty()); - assertThat("Reporter log spans", jBuilder.reporterLogSpans(), nullValue()); - assertThat("Reporter max queue size", jBuilder.reporterMaxQueueSize(), nullValue()); - assertThat("Reporter flush intervals", jBuilder.reporterFlushIntervalMillis(), nullValue()); - assertThat("Sampler type", jBuilder.samplerType(), nullValue()); - assertThat("Sampler param", jBuilder.samplerParam(), nullValue()); - assertThat("Sampler manager", jBuilder.samplerManager(), nullValue()); - - // I should not build the tracer, as that may try connecting to other endpoints - // only test the configuration I modify - Configuration jaegerConfig = jBuilder.jaegerConfig(); - // only endpoint is configured - Configuration.SenderConfiguration sc = jaegerConfig.getReporter().getSenderConfiguration(); - assertThat(sc.getEndpoint(), is("http://" + DEFAULT_HTTP_HOST + ":" + DEFAULT_HTTP_PORT + DEFAULT_HTTP_PATH)); + assertThat("Sampler type", jBuilder.samplerType(), is(JaegerTracerBuilder.SamplerType.CONSTANT)); + assertThat("Sampler param", jBuilder.samplerParam(), is(Integer.valueOf(1))); } @Test @@ -99,25 +77,8 @@ void testConfigDisabled() { assertThat(jBuilder.serviceName(), is("helidon-service")); - Tracer tracer = builder.build().unwrap(Tracer.class); - assertThat(tracer, instanceOf(NoopTracer.class)); - } - - @Test - void testConfigUdp() { - TracerBuilder builder = TracerBuilder.create(config.get("jaeger-udp")); - - JaegerTracerBuilder jBuilder = builder.unwrap(JaegerTracerBuilder.class); - assertThat(jBuilder.serviceName(), is("udp-service")); - - // I should not build the tracer, as that may try connecting to other endpoints - // only test the configuration I modify - Configuration jaegerConfig = jBuilder.jaegerConfig(); - // only endpoint is configured - Configuration.SenderConfiguration sc = jaegerConfig.getReporter().getSenderConfiguration(); - assertThat(sc.getAgentHost(), is("192.168.1.2")); - assertThat(sc.getAgentPort(), is(14268)); - assertThat(sc.getEndpoint(), is(nullValue())); + Tracer tracer = builder.build(); + assertThat(tracer, sameInstance(Tracer.noOp())); } @Test @@ -133,16 +94,8 @@ void testFullHttp() { assertThat("Host", jBuilder.host(), is("192.168.1.3")); assertThat("Port", jBuilder.port(), is(14240)); assertThat("Path", jBuilder.path(), is("/api/traces/mine")); - assertThat("Token", jBuilder.token(), is("token")); - assertThat("Username", jBuilder.username(), is("user")); - assertThat("Password", jBuilder.password(), is("pass")); - assertThat("Propagations", jBuilder.propagations(), is(List.of(Configuration.Propagation.JAEGER))); - assertThat("Reporter log spans", jBuilder.reporterLogSpans(), is(false)); - assertThat("Reporter max queue size", jBuilder.reporterMaxQueueSize(), is(42)); - assertThat("Reporter flush intervals", jBuilder.reporterFlushIntervalMillis(), is(10001L)); - assertThat("Sampler type", jBuilder.samplerType(), is(JaegerTracerBuilder.SamplerType.REMOTE)); + assertThat("Sampler type", jBuilder.samplerType(), is(JaegerTracerBuilder.SamplerType.RATIO)); assertThat("Sampler param", jBuilder.samplerParam(), is(0.5)); - assertThat("Sampler manager", jBuilder.samplerManager(), is("localhost:47877")); assertThat("Tags", jBuilder.tags(), is(Map.of( "tag1", "tag1-value", "tag2", "tag2-value", @@ -152,46 +105,5 @@ void testFullHttp() { "tag6", "741" ))); - // I should not build the tracer, as that may try connecting to other endpoints - // only test the configuration I modify - Configuration jaegerConfig = jBuilder.jaegerConfig(); - Configuration.ReporterConfiguration reporter = jaegerConfig.getReporter(); - assertThat(reporter.getFlushIntervalMs(), is(10001)); - assertThat(reporter.getLogSpans(), is(false)); - assertThat(reporter.getMaxQueueSize(), is(42)); - - Configuration.SenderConfiguration sc = reporter.getSenderConfiguration(); - // we should have endpoint, as we use https - assertThat(sc.getEndpoint(), is("https://192.168.1.3:14240/api/traces/mine")); - // if both are specified, token has precedence - assertThat(sc.getAuthToken(), is("token")); - assertThat(sc.getAuthUsername(), nullValue()); - assertThat(sc.getAuthPassword(), nullValue()); - - - Configuration.SamplerConfiguration sampler = jaegerConfig.getSampler(); - assertThat(sampler.getManagerHostPort(), is("localhost:47877")); - assertThat(sampler.getParam(), is(0.5)); - assertThat(sampler.getType(), is("remote")); - - assertThat(jaegerConfig.getTracerTags(), is(Map.of( - "tag1", "tag1-value", - "tag2", "tag2-value", - "tag3", "true", - "tag4", "false", - "tag5", "145", - "tag6", "741" - ))); - } - - @Test - void testJaegerPropagations() { - TracerBuilder builder = TracerBuilder.create(config.get("jaeger-propagations")); - JaegerTracerBuilder jBuilder = builder.unwrap(JaegerTracerBuilder.class); - - assertThat(jBuilder.serviceName(), is("helidon-propagations")); - assertThat("Propagations", - jBuilder.propagations(), - is(List.of(Configuration.Propagation.JAEGER, Configuration.Propagation.B3))); } } \ No newline at end of file diff --git a/tracing/jaeger/src/test/resources/application.yaml b/tracing/jaeger/src/test/resources/application.yaml index e9680e6e2df..df7ce32fbdc 100644 --- a/tracing/jaeger/src/test/resources/application.yaml +++ b/tracing/jaeger/src/test/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, 2021 Oracle and/or its affiliates. +# Copyright (c) 2019, 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. @@ -33,16 +33,8 @@ tracing: host: "192.168.1.3" # JAEGER_ENDPOINT port: 14240 # JAEGER_ENDPOINT path: "/api/traces/mine" # JAEGER_ENDPOINT - token: "token" # JAEGER_AUTH_TOKEN - username: "user" # JAEGER_USER - password: "pass" # JAEGER_PASSWORD - propagation: "jaeger" # JAEGER_PROPAGATION either "jaeger" or "b3" - log-spans: false # JAEGER_REPORTER_LOG_SPANS - max-queue-size: 42 # JAEGER_REPORTER_MAX_QUEUE_SIZE - flush-interval-ms: 10001 # JAEGER_REPORTER_FLUSH_INTERVAL - sampler-type: "remote"# JAEGER_SAMPLER_TYPE (https://www.jaegertracing.io/docs/latest/sampling/#client-sampling-configuration) - sampler-param: 0.5 # JAEGER_SAMPLER_PARAM (number) - sampler-manager: "localhost:47877" # JAEGER_SAMPLER_MANAGER_HOST_PORT + sampler-type: "ratio" + sampler-param: 0.5 tags: tag1: "tag1-value" # JAEGER_TAGS tag2: "tag2-value" # JAEGER_TAGS @@ -52,6 +44,3 @@ tracing: int-tags: tag5: 145 # JAEGER_TAGS tag6: 741 # JAEGER_TAGS - jaeger-propagations: - service: "helidon-propagations" - propagation: ["jaeger", "b3"] diff --git a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingFilter.java b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingFilter.java index b6c3fa59ee1..c70f58331cf 100644 --- a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingFilter.java +++ b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingFilter.java @@ -28,6 +28,7 @@ import io.helidon.common.serviceloader.HelidonServiceLoader; import io.helidon.tracing.HeaderConsumer; import io.helidon.tracing.HeaderProvider; +import io.helidon.tracing.Scope; import io.helidon.tracing.Span; import io.helidon.tracing.SpanContext; import io.helidon.tracing.Tag; @@ -128,6 +129,7 @@ public class ClientTracingFilter implements ClientRequestFilter, ClientResponseF */ public static final String X_REQUEST_ID = "x-request-id"; static final String SPAN_PROPERTY_NAME = ClientTracingFilter.class.getName() + ".span"; + static final String SPAN_SCOPE_PROPERTY_NAME = ClientTracingFilter.class.getName() + ".span-scope"; /** * Name of the configuration of a span created for outbound calls. */ @@ -172,7 +174,7 @@ public void filter(ClientRequestContext requestContext) { } Tracer tracer = findTracer(requestContext, tracingContext); - Optional parentSpan = findParentSpan(tracer, requestContext, tracingContext); + Optional parentSpan = findParentSpan(requestContext, tracingContext); Map> inboundHeaders = findInboundHeaders(tracingContext); String spanName = findSpanName(requestContext, spanConfig); @@ -182,8 +184,11 @@ public void filter(ClientRequestContext requestContext) { parentSpan, spanName); + Scope spanScope = currentSpan.activate(); + // register it so we can close the span on response requestContext.setProperty(SPAN_PROPERTY_NAME, currentSpan); + requestContext.setProperty(SPAN_SCOPE_PROPERTY_NAME, spanScope); // and also register it with Context, so we can close the span in case of an exception that does not hit the // response filter @@ -207,10 +212,10 @@ public void filter(ClientRequestContext requestContext) { @Override public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) { - Object property = requestContext.getProperty(SPAN_PROPERTY_NAME); + Object spanProperty = requestContext.getProperty(SPAN_PROPERTY_NAME); + Object scopeProperty = requestContext.getProperty(SPAN_SCOPE_PROPERTY_NAME); - if (property instanceof Span) { - Span span = (Span) property; + if (spanProperty instanceof Span span) { int status = responseContext.getStatus(); Tag.HTTP_STATUS.create(status).apply(span); if (status >= HTTP_STATUS_ERROR_THRESHOLD) { @@ -220,7 +225,13 @@ public void filter(ClientRequestContext requestContext, ClientResponseContext re "error.kind", (status < HTTP_STATUS_SERVER_ERROR_THRESHOLD) ? "ClientError" : "ServerError")); } + + if (scopeProperty instanceof Scope scope) { + scope.close(); + } span.end(); + + requestContext.removeProperty(SPAN_SCOPE_PROPERTY_NAME); requestContext.removeProperty(SPAN_PROPERTY_NAME); } } @@ -261,7 +272,7 @@ private Map> updateOutboundHeaders(Map return result; } - private Optional findParentSpan(Tracer tracer, ClientRequestContext requestContext, + private Optional findParentSpan(ClientRequestContext requestContext, Optional tracingContext) { // parent span lookup @@ -272,18 +283,16 @@ private Optional findParentSpan(Tracer tracer, ClientRequestContext } // then the active span - Span activeSpan = Span.current().orElse(null); - if (null != activeSpan) { - return Optional.of(activeSpan.context()); - } - - // then spans registered in context - return // from injected span context - tracingContext.map(TracingContext::parentSpan) + return Span.current() + .map(Span::context) + .or(() -> + // then spans registered in context + // from injected span context + tracingContext.map(TracingContext::parentSpan) // first look for "our" span context (e.g. one registered by a component that is aware that we exist) .or(() -> Contexts.context().flatMap(ctx -> ctx.get(ClientTracingFilter.class, SpanContext.class))) // then look for overall span context - .or(() -> Contexts.context().flatMap(ctx -> ctx.get(SpanContext.class))); + .or(() -> Contexts.context().flatMap(ctx -> ctx.get(SpanContext.class)))); } private String findSpanName(ClientRequestContext requestContext, SpanTracingConfig spanConfig) { diff --git a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java index f066aa475cf..0f97a43603d 100644 --- a/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java +++ b/tracing/jersey-client/src/main/java/io/helidon/tracing/jersey/client/ClientTracingInterceptor.java @@ -15,6 +15,7 @@ */ package io.helidon.tracing.jersey.client; +import io.helidon.tracing.Scope; import io.helidon.tracing.Span; import jakarta.ws.rs.client.ClientRequestContext; @@ -22,6 +23,7 @@ import org.glassfish.jersey.client.spi.PostInvocationInterceptor; import static io.helidon.tracing.jersey.client.ClientTracingFilter.SPAN_PROPERTY_NAME; +import static io.helidon.tracing.jersey.client.ClientTracingFilter.SPAN_SCOPE_PROPERTY_NAME; /** * A post-invocation client interceptor. If an exception (e.g. a connection timeout) @@ -45,12 +47,18 @@ public void afterRequest(ClientRequestContext requestContext, ClientResponseCont */ @Override public void onException(ClientRequestContext requestContext, ExceptionContext exceptionContext) { - Object property = requestContext.getProperty(SPAN_PROPERTY_NAME); - if (property instanceof Span span) { + Object spanProperty = requestContext.getProperty(SPAN_PROPERTY_NAME); + Object scopeProperty = requestContext.getProperty(SPAN_SCOPE_PROPERTY_NAME); + + if (spanProperty instanceof Span span) { span.status(Span.Status.ERROR); span.end(exceptionContext.getThrowables().pop()); requestContext.removeProperty(SPAN_PROPERTY_NAME); } + if (scopeProperty instanceof Scope scope) { + scope.close(); + requestContext.removeProperty(SPAN_SCOPE_PROPERTY_NAME); + } for (Throwable throwable : exceptionContext.getThrowables()) { throwable.printStackTrace(); } diff --git a/tracing/opentelemetry/pom.xml b/tracing/opentelemetry/pom.xml index be85448b825..cae4ebe8098 100644 --- a/tracing/opentelemetry/pom.xml +++ b/tracing/opentelemetry/pom.xml @@ -44,6 +44,10 @@ io.opentelemetry opentelemetry-semconv + + io.opentelemetry + opentelemetry-extension-trace-propagators + org.junit.jupiter junit-jupiter-api diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java index bdfb6947610..2d074232265 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/HelidonOpenTelemetry.java @@ -16,6 +16,8 @@ package io.helidon.tracing.opentelemetry; +import java.util.Map; + import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; @@ -30,11 +32,12 @@ private HelidonOpenTelemetry() { * Wrap an open telemetry tracer. * * @param telemetry open telemetry instance - * @param tracer tracer + * @param tracer tracer + * @param tags tracer tags * @return Helidon {@link io.helidon.tracing.Tracer} */ - public static OpenTelemetryTracer create(OpenTelemetry telemetry, Tracer tracer) { - return new OpenTelemetryTracer(telemetry, tracer); + public static OpenTelemetryTracer create(OpenTelemetry telemetry, Tracer tracer, Map tags) { + return new OpenTelemetryTracer(telemetry, tracer, tags); } /** diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryScope.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryScope.java index e7078994f4f..5d092f283fb 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryScope.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryScope.java @@ -15,10 +15,13 @@ */ package io.helidon.tracing.opentelemetry; +import java.util.concurrent.atomic.AtomicBoolean; + import io.helidon.tracing.Scope; class OpenTelemetryScope implements Scope { private final io.opentelemetry.context.Scope delegate; + private final AtomicBoolean closed = new AtomicBoolean(); OpenTelemetryScope(io.opentelemetry.context.Scope scope) { delegate = scope; @@ -26,6 +29,13 @@ class OpenTelemetryScope implements Scope { @Override public void close() { - delegate.close(); + if (closed.compareAndSet(false, true) && delegate != null) { + delegate.close(); + } + } + + @Override + public boolean isClosed() { + return closed.get(); } } diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpan.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpan.java index ff8654f35f1..12a7c1a3972 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpan.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpan.java @@ -24,6 +24,7 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; class OpenTelemetrySpan implements io.helidon.tracing.Span { private final Span delegate; @@ -63,7 +64,7 @@ public void status(Status status) { @Override public SpanContext context() { - return new OpenTelemetrySpanContext(delegate.getSpanContext()); + return new OpenTelemetrySpanContext(Context.current().with(delegate)); } @Override diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanBuilder.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanBuilder.java index b67db5e0b7b..fa3e9e2bdc8 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanBuilder.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanBuilder.java @@ -25,6 +25,7 @@ class OpenTelemetrySpanBuilder implements Span.Builder { private final SpanBuilder spanBuilder; + private boolean parentSet; OpenTelemetrySpanBuilder(SpanBuilder spanBuilder) { this.spanBuilder = spanBuilder; @@ -37,7 +38,8 @@ public Span build() { @Override public OpenTelemetrySpanBuilder parent(SpanContext spanContext) { - spanBuilder.addLink(((OpenTelemetrySpanContext) spanContext).openTelemetry()); + this.parentSet = true; + spanContext.asParent(this); return this; } @@ -79,8 +81,15 @@ public OpenTelemetrySpanBuilder tag(String key, Number value) { @Override public Span start(Instant instant) { + if (!parentSet) { + spanBuilder.setNoParent(); + } spanBuilder.setStartTimestamp(instant); io.opentelemetry.api.trace.Span span = spanBuilder.startSpan(); return new OpenTelemetrySpan(span); } + + SpanBuilder openTelemetry() { + return spanBuilder; + } } diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanContext.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanContext.java index f25cd9e79b0..16376b01103 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanContext.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetrySpanContext.java @@ -17,24 +17,34 @@ import io.helidon.tracing.SpanContext; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; + class OpenTelemetrySpanContext implements SpanContext { - private final io.opentelemetry.api.trace.SpanContext delegate; + private final Context context; - OpenTelemetrySpanContext(io.opentelemetry.api.trace.SpanContext context) { - this.delegate = context; + OpenTelemetrySpanContext(Context context) { + this.context = context; } @Override public String traceId() { - return delegate.getTraceId(); + return Span.fromContext(context).getSpanContext().getTraceId(); } @Override public String spanId() { - return delegate.getSpanId(); + return Span.fromContext(context).getSpanContext().getSpanId(); + } + + @Override + public void asParent(io.helidon.tracing.Span.Builder spanBuilder) { + spanBuilder.unwrap(OpenTelemetrySpanBuilder.class) + .openTelemetry() + .setParent(context); } - io.opentelemetry.api.trace.SpanContext openTelemetry() { - return delegate; + public Context openTelemetry() { + return context; } } diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracer.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracer.java index 308c086d0fc..b74e1c7abeb 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracer.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracer.java @@ -15,6 +15,7 @@ */ package io.helidon.tracing.opentelemetry; +import java.util.Map; import java.util.Optional; import io.helidon.config.Config; @@ -39,12 +40,14 @@ class OpenTelemetryTracer implements Tracer { private final io.opentelemetry.api.trace.Tracer delegate; private final boolean enabled; private final TextMapPropagator propagator; + private final Map tags; - OpenTelemetryTracer(OpenTelemetry telemetry, io.opentelemetry.api.trace.Tracer tracer) { + OpenTelemetryTracer(OpenTelemetry telemetry, io.opentelemetry.api.trace.Tracer tracer, Map tags) { this.telemetry = telemetry; this.delegate = tracer; this.enabled = !tracer.getClass().getSimpleName().equals("DefaultTracer"); this.propagator = telemetry.getPropagators().getTextMapPropagator(); + this.tags = tags; } static Builder builder() { @@ -58,23 +61,31 @@ public boolean enabled() { @Override public Span.Builder spanBuilder(String name) { - return new OpenTelemetrySpanBuilder(delegate.spanBuilder(name)); + OpenTelemetrySpanBuilder builder = new OpenTelemetrySpanBuilder(delegate.spanBuilder(name)); + tags.forEach(builder::tag); + return builder; } @Override public Optional extract(HeaderProvider headersProvider) { Context context = propagator.extract(Context.current(), headersProvider, GETTER); - context.makeCurrent(); - return Optional.ofNullable(io.opentelemetry.api.trace.Span.current()) - .map(io.opentelemetry.api.trace.Span::getSpanContext) + return Optional.ofNullable(context) .map(OpenTelemetrySpanContext::new); } @Override public void inject(SpanContext spanContext, HeaderProvider inboundHeadersProvider, HeaderConsumer outboundHeadersConsumer) { - io.opentelemetry.api.trace.Span.wrap(((OpenTelemetrySpanContext) spanContext).openTelemetry()).makeCurrent(); - propagator.inject(Context.current(), outboundHeadersConsumer, SETTER); + propagator.inject(((OpenTelemetrySpanContext) spanContext).openTelemetry(), outboundHeadersConsumer, SETTER); + } + + @Override + public T unwrap(Class tracerClass) { + if (tracerClass.isAssignableFrom(delegate.getClass())) { + return tracerClass.cast(delegate); + } + throw new IllegalArgumentException("Cannot provide an instance of " + tracerClass.getName() + + ", telemetry tracer is: " + delegate.getClass().getName()); } static class Builder implements TracerBuilder { @@ -88,7 +99,7 @@ public Tracer build() { ot = GlobalOpenTelemetry.get(); } io.opentelemetry.api.trace.Tracer tracer = ot.getTracer(serviceName); - Tracer result = new OpenTelemetryTracer(ot, tracer); + Tracer result = new OpenTelemetryTracer(ot, tracer, Map.of()); if (registerGlobal) { Tracer.global(result); } diff --git a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracerProvider.java b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracerProvider.java index b27f53ba26c..669826b854f 100644 --- a/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracerProvider.java +++ b/tracing/opentelemetry/src/main/java/io/helidon/tracing/opentelemetry/OpenTelemetryTracerProvider.java @@ -15,10 +15,13 @@ */ package io.helidon.tracing.opentelemetry; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import io.helidon.common.LazyValue; +import io.helidon.common.Prioritized; import io.helidon.common.context.Context; import io.helidon.common.context.Contexts; import io.helidon.tracing.Span; @@ -28,14 +31,17 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import jakarta.annotation.Priority; /** * Service loader provider implementation for {@link io.helidon.tracing.spi.TracerProvider}. */ +@Priority(Prioritized.DEFAULT_PRIORITY + 1000) public class OpenTelemetryTracerProvider implements TracerProvider { private static final System.Logger LOGGER = System.getLogger(OpenTelemetryTracerProvider.class.getName()); - private static final AtomicReference CONFIGURED_TRACER = new AtomicReference<>(); + private static final AtomicReference CONFIGURED_TRACER = new AtomicReference<>(); + private static final AtomicBoolean GLOBAL_SET = new AtomicBoolean(); private static final LazyValue GLOBAL_TRACER; static { @@ -54,11 +60,30 @@ public class OpenTelemetryTracerProvider implements TracerProvider { + "Tracer.global(HelidonOpenTelemetry.create(ot, tracer). Using global open telemetry"); } OpenTelemetry ot = GlobalOpenTelemetry.get(); - return new OpenTelemetryTracer(ot, ot.getTracer("helidon-service")); + return new OpenTelemetryTracer(ot, ot.getTracer("helidon-service"), Map.of()); }); }); } + /** + * Register global tracer. + * + * @param tracer global tracer + */ + public static void globalTracer(Tracer tracer) { + GLOBAL_SET.set(true); + CONFIGURED_TRACER.set(tracer); + } + + /** + * Registered global tracer, or tracer from global open telemetry. + * + * @return tracer + */ + public static Tracer globalTracer() { + return GLOBAL_TRACER.get(); + } + @Override public TracerBuilder createBuilder() { return OpenTelemetryTracer.builder(); @@ -66,13 +91,13 @@ public TracerBuilder createBuilder() { @Override public Tracer global() { - return GLOBAL_TRACER.get(); + return globalTracer(); } @Override public void global(Tracer tracer) { if (tracer instanceof OpenTelemetryTracer ott) { - CONFIGURED_TRACER.set(ott); + globalTracer(ott); } throw new IllegalArgumentException("Tracer must be an instance of Helidon OpenTelemetry tracer. " + "Please use HelidonOpenTelemetry to create such instance"); @@ -82,4 +107,9 @@ public void global(Tracer tracer) { public Optional currentSpan() { return Optional.ofNullable(io.opentelemetry.api.trace.Span.current()).map(HelidonOpenTelemetry::create); } + + @Override + public boolean available() { + return GLOBAL_SET.get(); + } } diff --git a/tracing/opentelemetry/src/main/java/module-info.java b/tracing/opentelemetry/src/main/java/module-info.java index 1bd3cd6fa52..d26b4bcdbbb 100644 --- a/tracing/opentelemetry/src/main/java/module-info.java +++ b/tracing/opentelemetry/src/main/java/module-info.java @@ -20,9 +20,11 @@ requires io.helidon.tracing; requires io.helidon.common.context; - requires io.opentelemetry.api; - requires io.opentelemetry.context; - requires io.opentelemetry.semconv; + requires transitive io.opentelemetry.api; + requires transitive io.opentelemetry.semconv; + requires transitive io.opentelemetry.context; + requires transitive io.opentelemetry.extension.trace.propagation; + requires io.helidon.common; requires io.helidon.config; diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracing.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracing.java index 6eff8b07b31..652ed6da7bd 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracing.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracing.java @@ -46,4 +46,5 @@ public static io.helidon.tracing.Tracer create(Tracer tracer) { public static Span create(Tracer tracer, io.opentracing.Span span) { return new OpenTracingSpan(tracer, span); } + } diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingContext.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingContext.java index 745312cf75b..4daf3af9bab 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingContext.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingContext.java @@ -15,6 +15,8 @@ */ package io.helidon.tracing.opentracing; +import io.helidon.tracing.Span; + import io.opentracing.SpanContext; class OpenTracingContext implements io.helidon.tracing.SpanContext { @@ -34,6 +36,12 @@ public String spanId() { return delegate.toSpanId(); } + @Override + public void asParent(Span.Builder spanBuilder) { + spanBuilder.unwrap(OpenTracingSpanBuilder.class) + .parent(this); + } + SpanContext openTracing() { return delegate; } diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingProviderHelper.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingProviderHelper.java index ad7f74437b9..746c130d555 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingProviderHelper.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingProviderHelper.java @@ -34,6 +34,10 @@ final class OpenTracingProviderHelper { private OpenTracingProviderHelper() { } + public static boolean available() { + return !TRACER_PROVIDER.createBuilder().getClass().equals(NoOpBuilder.class); + } + static OpenTracingProvider provider() { return TRACER_PROVIDER; } diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingScope.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingScope.java index d0a066badb6..03e17a2d059 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingScope.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingScope.java @@ -15,17 +15,27 @@ */ package io.helidon.tracing.opentracing; +import java.util.concurrent.atomic.AtomicBoolean; + import io.helidon.tracing.Scope; class OpenTracingScope implements Scope { - private final io.opentracing.Scope scope; + private final io.opentracing.Scope delegate; + private final AtomicBoolean closed = new AtomicBoolean(); OpenTracingScope(io.opentracing.Scope scope) { - this.scope = scope; + this.delegate = scope; + } + + @Override + public void close() { + if (closed.compareAndSet(false, true) && delegate != null) { + delegate.close(); + } } @Override - public void close() throws Exception { - scope.close(); + public boolean isClosed() { + return closed.get(); } } diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerProvider.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerProvider.java index 86bcaa189b3..18ec3245651 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerProvider.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerProvider.java @@ -17,6 +17,7 @@ import java.util.Optional; +import io.helidon.common.Prioritized; import io.helidon.tracing.Span; import io.helidon.tracing.Tracer; import io.helidon.tracing.TracerBuilder; @@ -24,11 +25,13 @@ import io.opentracing.noop.NoopSpan; import io.opentracing.util.GlobalTracer; +import jakarta.annotation.Priority; /** * {@link java.util.ServiceLoader} service implementation of {@link io.helidon.tracing.spi.TracerProvider} for Open Tracing * tracers. */ +@Priority(Prioritized.DEFAULT_PRIORITY + 2000) public class OpenTracingTracerProvider implements TracerProvider { @Override public TracerBuilder createBuilder() { @@ -54,4 +57,9 @@ public Optional currentSpan() { .flatMap(it -> it instanceof NoopSpan ? Optional.empty() : Optional.of(it)) .map(it -> new OpenTracingSpan(tracer, it)); } + + @Override + public boolean available() { + return OpenTracingProviderHelper.available(); + } } diff --git a/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java b/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java index 298eb2b0ac2..782822aeaa6 100644 --- a/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java +++ b/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java @@ -16,6 +16,7 @@ package io.helidon.tracing.tests.it1; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; @@ -53,52 +54,30 @@ /** * The ZipkinClientTest. */ -public class OpentraceableClientE2ETest { - - private static WebServer server; +class OpentraceableClientE2ETest { private static final int EXPECTED_TRACE_EVENTS_COUNT = 4; private static final CountDownLatch EVENTS_LATCH = new CountDownLatch(EXPECTED_TRACE_EVENTS_COUNT); private static final Map EVENTS_MAP = new ConcurrentHashMap<>(); - + private static WebServer server; private static Client client; - /** Use custom {@link Tracer} that adds events to {@link #EVENTS_MAP} map. */ - private static io.helidon.tracing.Tracer tracer(String serviceName) { - Tracing braveTracing = Tracing.newBuilder() - .localServiceName(serviceName) - .spanReporter(span -> { - EVENTS_MAP.put(span.id(), span); - EVENTS_LATCH.countDown(); - }) - .build(); - - // use this to create an OpenTracing Tracer - return OpenTracing.create(new ZipkinTracer(BraveTracer.create(braveTracing), List.of())); - } - - private static WebServer startWebServer() throws InterruptedException, ExecutionException, TimeoutException { - return WebServer.builder() - .host("localhost") - .routing(Routing.builder() - .any((req, res) -> res.send("OK"))) - .tracer(tracer("test-server")) - .build() - .start() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); + @BeforeAll + static void startServerInitClient() throws Exception { + server = startWebServer(); + client = ClientBuilder.newClient(new ClientConfig(ClientTracingFilter.class)); } @Test - public void e2e() throws Exception { + void e2e() throws Exception { io.helidon.tracing.Tracer tracer = tracer("test-client"); Span start = tracer.spanBuilder("client-call") - .start(); + .start(); Response response = client.target("http://localhost:" + server.port()) - .property(ClientTracingFilter.TRACER_PROPERTY_NAME, tracer) - .property(ClientTracingFilter.CURRENT_SPAN_CONTEXT_PROPERTY_NAME, start.context()) - .request() - .get(); + .property(ClientTracingFilter.TRACER_PROPERTY_NAME, tracer) + .property(ClientTracingFilter.CURRENT_SPAN_CONTEXT_PROPERTY_NAME, start.context()) + .request() + .get(); assertThat(response.getStatus(), is(200)); @@ -114,23 +93,43 @@ public void e2e() throws Exception { assertThat(EVENTS_MAP.entrySet(), hasSize(0)); } - @BeforeAll - public static void startServerInitClient() throws Exception { - server = startWebServer(); - client = ClientBuilder.newClient(new ClientConfig(ClientTracingFilter.class)); - } - @AfterEach public void stopAndClose() throws Exception { if (server != null) { server.shutdown() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); } client.close(); } + /** + * Use custom {@link Tracer} that adds events to {@link #EVENTS_MAP} map. + */ + private static io.helidon.tracing.Tracer tracer(String serviceName) { + Tracing braveTracing = Tracing.newBuilder() + .localServiceName(serviceName) + .spanReporter(span -> { + EVENTS_MAP.put(span.id(), span); + EVENTS_LATCH.countDown(); + }) + .build(); + + // use this to create an OpenTracing Tracer + return OpenTracing.create(new ZipkinTracer(BraveTracer.create(braveTracing), List.of())); + } + + private static WebServer startWebServer() throws InterruptedException, ExecutionException, TimeoutException { + return WebServer.builder() + .host("localhost") + .routing(Routing.builder() + .any((req, res) -> res.send("OK"))) + .tracer(tracer("test-server")) + .build() + .start() + .await(Duration.ofSeconds(10)); + } private String printSpans(Map spans) { StringBuilder sb = new StringBuilder(); @@ -146,7 +145,9 @@ private String printSpans(Map spans) { return sb.toString(); } - /** Assert that all the spans are in a strict {@code parent-child-grandchild-[grandgrandchild]-[...]} relationship. */ + /** + * Assert that all the spans are in a strict {@code parent-child-grandchild-[grandgrandchild]-[...]} relationship. + */ private void assertSpanChain(zipkin2.Span topSpan, Map spans) { if (spans.isEmpty()) { // end the recursion @@ -154,16 +155,16 @@ private void assertSpanChain(zipkin2.Span topSpan, Map spa } Optional removeSpan = findAndRemoveSpan(topSpan.id(), spans); assertSpanChain(removeSpan.orElseThrow( - () -> new AssertionError("Span with parent ID not found: " + topSpan.id() + " at: " + printSpans(spans))), + () -> new AssertionError("Span with parent ID not found: " + topSpan.id() + " at: " + printSpans(spans))), spans); } private Optional findAndRemoveSpan(String id, Map spans) { Optional span = spans.entrySet() - .stream() - .filter(entry -> id.equals(entry.getValue().parentId())) - .map(Map.Entry::getValue) - .findFirst(); + .stream() + .filter(entry -> id.equals(entry.getValue().parentId())) + .map(Map.Entry::getValue) + .findFirst(); span.ifPresent(span1 -> spans.remove(span1.id())); return span; diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpBuilder.java b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpBuilder.java index fae092c9666..2f9fbdbe4d6 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpBuilder.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpBuilder.java @@ -23,7 +23,6 @@ * No op tracer builder - used when there is no tracer service available. */ final class NoOpBuilder implements TracerBuilder { - private static final Tracer TRACER = new NoOpTracer(); private NoOpBuilder() { } @@ -92,7 +91,7 @@ public NoOpBuilder registerGlobal(boolean global) { @Override public Tracer build() { - return TRACER; + return NoOpTracer.instance(); } @Override diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracer.java b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracer.java index fd6e02054a1..f4e4d7d7bf4 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracer.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracer.java @@ -20,12 +20,20 @@ import java.util.Optional; class NoOpTracer implements Tracer { - static final SpanContext SPAN_CONTEXT = new SpanContext(); + private static final NoOpTracer INSTANCE = new NoOpTracer(); + private static final SpanContext SPAN_CONTEXT = new SpanContext(); private static final Builder BUILDER = new Builder(); private static final Span SPAN = new Span(); private static final Scope SCOPE = new Scope(); + private NoOpTracer() { + } + + static Tracer instance() { + return INSTANCE; + } + @Override public boolean enabled() { return false; @@ -140,11 +148,20 @@ public String traceId() { public String spanId() { return "no-op"; } + + @Override + public void asParent(io.helidon.tracing.Span.Builder spanBuilder) { + } } private static class Scope implements io.helidon.tracing.Scope { @Override - public void close() throws Exception { + public void close() { + } + + @Override + public boolean isClosed() { + return true; } } } diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracerProvider.java b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracerProvider.java index daad4196d83..5bccf9200fb 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracerProvider.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/NoOpTracerProvider.java @@ -40,4 +40,9 @@ public void global(Tracer tracer) { public Optional currentSpan() { return Optional.empty(); } + + @Override + public boolean available() { + return false; + } } diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/Scope.java b/tracing/tracing/src/main/java/io/helidon/tracing/Scope.java index 5b9fd81721d..931f22f4931 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/Scope.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/Scope.java @@ -19,4 +19,12 @@ * A Scope that can be (eventually) closed. Used when making a span active. */ public interface Scope extends AutoCloseable { + @Override + void close(); + + /** + * Whether the method {@link #close()} was already called or not. + * @return if this scope is closed + */ + boolean isClosed(); } diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/SpanContext.java b/tracing/tracing/src/main/java/io/helidon/tracing/SpanContext.java index acc3a665947..91192ac363b 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/SpanContext.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/SpanContext.java @@ -32,4 +32,11 @@ public interface SpanContext { * @return span id */ String spanId(); + + /** + * Configure this context as a parent of the provided builder. + * + * @param spanBuilder span builder to update, it will be a child of this span context + */ + void asParent(Span.Builder spanBuilder); } diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/Tracer.java b/tracing/tracing/src/main/java/io/helidon/tracing/Tracer.java index 7a8c12af4b9..a92d06ff540 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/Tracer.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/Tracer.java @@ -28,7 +28,7 @@ public interface Tracer { * @return no-op tracer */ static Tracer noOp() { - return new NoOpTracer(); + return NoOpTracer.instance(); } /** diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java b/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java index 264c65df920..3fd01aa0720 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/TracerProviderHelper.java @@ -15,6 +15,7 @@ */ package io.helidon.tracing; +import java.util.List; import java.util.Optional; import java.util.ServiceLoader; @@ -31,11 +32,21 @@ final class TracerProviderHelper { static { TracerProvider provider = null; try { - provider = HelidonServiceLoader.builder(ServiceLoader.load(TracerProvider.class)) + List allProviders = HelidonServiceLoader.builder(ServiceLoader.load(TracerProvider.class)) .addService(new NoOpTracerProvider(), 100000) .build() - .asList() - .get(0); + .asList(); + + if (allProviders.size() == 1 || allProviders.size() == 2) { + // only noop or one and noop + provider = allProviders.get(0); + } + for (TracerProvider aProvider : allProviders) { + if (aProvider.available()) { + provider = aProvider; + break; + } + } } catch (Throwable e) { LOGGER.log(System.Logger.Level.WARNING, "Failed to set up tracer provider, using no-op", e); } diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/spi/TracerProvider.java b/tracing/tracing/src/main/java/io/helidon/tracing/spi/TracerProvider.java index d2101690a23..daa480368bb 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/spi/TracerProvider.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/spi/TracerProvider.java @@ -54,4 +54,12 @@ public interface TracerProvider { * @return current span, or empty optional if current span cannot be found */ Optional currentSpan(); + + /** + * Whether there is a tracer available by this provider. + * This allows co-existence of multiple tracing providers within the same VM. + * + * @return whether this tracer provider has a tracer available + */ + boolean available(); } diff --git a/tracing/tracing/src/test/java/io/helidon/tracing/TracerBuilderTest.java b/tracing/tracing/src/test/java/io/helidon/tracing/TracerBuilderTest.java index 5533079fde7..4d41188763b 100644 --- a/tracing/tracing/src/test/java/io/helidon/tracing/TracerBuilderTest.java +++ b/tracing/tracing/src/test/java/io/helidon/tracing/TracerBuilderTest.java @@ -202,7 +202,7 @@ public B unwrap(Class builderClass) { @Override public Tracer build() { - tracer = new NoOpTracer(); + tracer = NoOpTracer.instance(); return tracer; } } From 1f503d981ea760001998d47c4042cf2cdbdfa26b Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Wed, 13 Jul 2022 11:32:16 -0400 Subject: [PATCH 05/51] New formatting for Helidon Health Checks SE document (#4495) * New formatting for Helidon Health Checks SE document. Signed-off-by: Santiago Pericasgeertsen * Incorporated suggested stylistic changes. Fixed references to new health doc. Signed-off-by: Santiago Pericasgeertsen --- docs/includes/pages.adoc | 2 +- docs/se/health.adoc | 517 ++++++++++++++++++++++++++++++ docs/se/health/health-in-k8s.adoc | 235 -------------- docs/se/health/health.adoc | 279 ---------------- docs/se/introduction.adoc | 2 +- docs/se/oci/object-storage.adoc | 2 +- docs/sitegen.yaml | 7 +- 7 files changed, 522 insertions(+), 522 deletions(-) create mode 100644 docs/se/health.adoc delete mode 100644 docs/se/health/health-in-k8s.adoc delete mode 100644 docs/se/health/health.adoc diff --git a/docs/includes/pages.adoc b/docs/includes/pages.adoc index 72031fb4f29..093c2ffbc63 100644 --- a/docs/includes/pages.adoc +++ b/docs/includes/pages.adoc @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////// ifdef::se-flavor[] -:health-page: {rootdir}/se/health/health.adoc +:health-page: {rootdir}/se/health.adoc :metrics-page: {rootdir}/se/metrics/metrics.adoc :openapi-page: {rootdir}/se/openapi.adoc endif::[] diff --git a/docs/se/health.adoc b/docs/se/health.adoc new file mode 100644 index 00000000000..8440f2ab5fe --- /dev/null +++ b/docs/se/health.adoc @@ -0,0 +1,517 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2019, 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. + +/////////////////////////////////////////////////////////////////////////////// + += Health Checks +:description: Helidon health checks +:keywords: helidon, health checks, health, check, readiness, liveness, probes, kubernetes +:feature-name: Health Checks +:rootdir: {docdir}/.. + +include::{rootdir}/includes/se.adoc[] + +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +It’s a good practice to monitor your microservice’s health, to ensure that it is +available and performs correctly. +Applications implement health checks to expose health status that is collected +at regular intervals by external tooling, such as orchestrators like +Kubernetes. The orchestrator may then take action, such as restarting your +application if the health check fails. + +A typical health check combines the statuses of all the dependencies that +affect availability and the ability to perform correctly: + +* network latency +* storage +* database +* other services used by your application + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.health + helidon-health + +---- + +Optional dependency to use built-in health checks: + +[source,xml] +---- + + io.helidon.health + helidon-health-checks + +---- + +== API + +A health check is a Java functional interface that returns a +`HealthCheckResponse` instance. You can choose to implement a health check +inline with a lambda expression or you can reference a method with the double +colon operator `::`. + +[source,java] +.Health check with a lambda expression: +---- +HealthCheck hc = () -> HealthCheckResponse + .named("exampleHealthCheck") + .up() + .build(); +---- + +[source,java] +.Health check with method reference: +---- +HealthCheckResponse exampleHealthCheck() { + return HealthCheckResponse + .named("exampleHealthCheck") + .up() + .build(); +} +HealthCheck hc = this::exampleHealthCheck; +---- + +`HealthSupport` is a WebServer service that contains a collection of +registered `HealthCheck` instances. When queried, it invokes the registered +health check and returns a response with a status code representing the overall +status of the application. + +[cols="1,5",role="flex, sm7"] +.Health status codes +|======= +| `200` | The application is healthy (with health check details in the response). +| `204` | The application is healthy (with _no_ health check details in the response). +| `503` | The application is not healthy. +| `500` | An error occurred while reporting the health. +|======= + +HTTP `GET` responses include JSON content showing the detailed results of all the health checks which the server executed after receiving the request. +HTTP `HEAD` requests return only the status with no payload. + +The following code snippets show how to register health checks while building an +instance of `HealthSupport`: + +[source,java] +.Create the health support service: +---- +HealthSupport health = HealthSupport.builder() + .addLiveness(hc) // hc created above + .build(); +---- + +[source,java] +.Create a custom health check: +---- +HealthSupport health = HealthSupport.builder() + .addLiveness(() -> HealthCheckResponse.named("exampleHealthCheck") + .up() + .withData("time", System.currentTimeMillis()) + .build()) + .build(); +---- + +The custom health check above returns a status of `UP` and the current time. +After creating the `HealthCheck` and registering it in a `HealthSupport`, we +must add the latter to the WebServer routes as follows: + +[source,java] +---- +Routing.builder() + .register(health) + .build(); +---- + +Here is a sample response to the custom health check registered above: + +[source,json] +.JSON response: +---- +{ + "status": "UP", + "checks": [ + { + "name": "exampleHealthCheck", + "status": "UP", + "data": { + "time": 1546958376613 + } + } + ] +} +---- + +TIP: Balance collecting a lot of information with the need to avoid overloading +the application and overwhelming users. + +The following table provides a summary of the Health Check API classes. + +[cols="4,6"] +.Health check API classes +|======= +| `org.eclipse.microprofile.health.HealthCheck` +| Java functional interface representing the logic of a single health check + +| `org.eclipse.microprofile.health.HealthCheckResponse` +| Result of a health check invocation that contains a status and a description. + +| `org.eclipse.microprofile.health.HealthCheckResponseBuilder` +| Builder class to create `HealthCheckResponse` instances + +| `io.helidon.health.HealthSupport` +| WebServer service that exposes `/health` and invokes the registered health +checks + +| `io.helidon.health.HealthSupport.Builder` +| Builder class to create `HealthSupport` instances +|======= + +=== Built-in health checks + +You can use Helidon-provided health checks to report various +common health check statuses: + +[[built-in-health-checks-table]] +[cols="1,1,3,15,3"] +|======= +|Built-in health check |Health check name |JavaDoc |Config properties |Default config value + +|deadlock detection +|`deadlock` +| link:{health-javadoc-base-url}/io/helidon/health/checks/DeadlockHealthCheck.html[`DeadlockHealthCheck`] +| n/a +| n/a + +|available disk space +|`diskSpace` +| link:{health-javadoc-base-url}/io/helidon/health/checks/DiskSpaceHealthCheck.html[`DiskSpaceHealthCheck`] +|`helidon.healthCheck.diskSpace.thresholdPercent` + ++ +`helidon.healthCheck.diskSpace.path` +| `99.999` + ++ +`/` +|available heap memory +| `heapMemory` +| link:{health-javadoc-base-url}/io/helidon/health/checks/HeapMemoryHealthCheck.html[`HeapMemoryHealthCheck`] +|`helidon.healthCheck.heapMemory.thresholdPercent` +|`98` +|======= + +The following code adds the default built-in health checks to your application: + +[source,java] +---- +HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) // <1> + .build(); + +Routing.builder() + .register(health) // <2> + .build(); +---- +<1> Add built-in health checks using defaults (requires the `helidon-health-checks` +dependency). +<2> Register the created `HealthSupport` with web server routing (adds the +`/health` endpoint). + +You can control the thresholds for built-in health checks in either of two ways: + +* Create the health checks individually +using their builders instead of using the `HealthChecks` convenience class. +Follow the JavaDoc links in the <> above. + +* Using configuration as explained in <>. + +=== Kubernetes probes + +Probes is the term used by Kubernetes to describe health checks for containers +(link:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes[Kubernetes documentation]). + +There are three types of probes: + +* _liveness_: Indicates whether the container is running +* _readiness_: Indicates whether the container is ready to service requests +* _startup_: Indicates whether the application in the container has started + +You can implement probes using the following mechanisms: + +. Running a command inside a container +. Sending an `HTTP` request to a container +. Opening a `TCP` socket to a container + +A microservice exposed to HTTP traffic will typically implement both the +liveness probe and the readiness probe using HTTP requests. +If the microservice takes a significant time to initialize itself, you can also define a startup probe, in which case +Kubernetes does not check liveness or readiness probes until the startup probe returns success. + +You can configure several parameters for probes. The following are the most +relevant parameters: + +[cols="2,5",role="flex, sm7"] +|======= +| `initialDelaySeconds` +| Number of seconds after the container has started before liveness or readiness +probes are initiated. + +| `periodSeconds` +| Probe interval. Default to 10 seconds. Minimum value is 1. + +| `timeoutSeconds` +| Number of seconds after which the probe times out. Defaults to 1 second. +Minimum value is 1 + +| `failureThreshold` +| Number of consecutive failures after which the probe should stop. Default: 3. +Minimum: 1. +|======= + +==== Liveness probe + +The liveness probe is used to verify the container has become unresponsive. +For example, it can be used to detect deadlocks or analyze heap usage. When +Kubernetes gives up on a liveness probe, the corresponding pod is restarted. + +NOTE: The liveness probe can result in repeated restarts in certain cases. +For example, if the probe is implemented to check all the dependencies +strictly, then it can fail repeatedly for temporary issues. Repeated restarts +can also occur if `timeoutSeconds` or `periodSeconds` is too low. + +We recommend the following: + +* Avoid checking dependencies in a liveness probe. +* Set `timeoutSeconds` to avoid excessive probe failures. +* Acknowledge startup times with `initialDelaySeconds`. + +==== Readiness probe + +The readiness probe is used to avoid routing requests to the pod until it is +ready to accept traffic. When Kubernetes gives up on a readiness probe, the +pod is not restarted, traffic is not routed to the pod anymore. + +NOTE: In certain cases, the readiness probe can cause all the pods to be removed +from service routing. For example, if the probe is implemented to check all the +dependencies strictly, then it can fail repeatedly for temporary issues. This +issue can also occur if `timeoutSeconds` or `periodSeconds` is too low. + +We recommend the following: + +* Be conservative when checking shared dependencies. +* Be aggressive when checking local dependencies. +* Set `failureThreshold` according to `periodSeconds` in order to accommodate +temporary errors. + +==== Startup probe + +The startup probe prevents Kubernetes from prematurely checking the other probes if the application takes a long time to start. +Otherwise, Kubernetes might misinterpret a failed liveness or readiness probe and shut down the container when, in fact, the application is still coming up. + + +=== Troubleshooting probes + +Failed probes are recorded as events associated with their corresponding pods. +The event message contains only the status code. + +[source,bash] +.Get the events of a single pod: +---- +POD_NAME=$(kubectl get pod -l app=acme -o jsonpath='{.items[0].metadata.name}') # <1> +kubectl get event --field-selector involvedObject.name=${POD_NAME} # <2> +---- +<1> Get the effective pod name by filtering pods with the label `app=acme`. +<2> Filter the events for the pod. + +TIP: Create log messages in your health check implementation when setting a +`DOWN` status. This will allow you to correlate the cause of a failed probe. + +== Configuration + +Built-in health checks can be configured using the config property keys +described in this +<>. Further, you can suppress one or more of the built-in +health checks by setting the configuration item +`helidon.health.exclude` to a comma-separated list of the health check names +(from this <>) you want to exclude. + +== Examples + +=== JSON response example + +Accessing the Helidon-provided `/health` endpoint reports the health of your application +as shown below: + +[source,json] +.JSON response. +---- +{ + "status": "UP", + "checks": [ + { + "name": "deadlock", + "status": "UP" + }, + { + "name": "diskSpace", + "status": "UP", + "data": { + "free": "211.00 GB", + "freeBytes": 226563444736, + "percentFree": "45.31%", + "total": "465.72 GB", + "totalBytes": 500068036608 + } + }, + { + "name": "heapMemory", + "status": "UP", + "data": { + "free": "215.15 MB", + "freeBytes": 225600496, + "max": "3.56 GB", + "maxBytes": 3817865216, + "percentFree": "99.17%", + "total": "245.50 MB", + "totalBytes": 257425408 + } + } + ] +} +---- + +=== Kubernetes example + +This example shows the usage of the Helidon health API in an application that +implements health endpoints for the liveness and readiness probes. Note that +the application code dissociates the health endpoints from the default routes, +so that the health endpoints are not exposed by the service. An example YAML +specification is also provided for the Kubernetes service and deployment. + +[source,java] +.Application code: +---- +Routing healthRouting = Routing.builder() + .register(JsonSupport.create()) + .register(HealthSupport.builder() + .webContext("/live") // <1> + .addLiveness(HealthChecks.healthChecks()) // <2> + .build()) + .register(HealthSupport.builder() + .webContext("/ready") // <3> + .addReadiness(() -> HealthCheckResponse.named("database").up().build()) // <4> + .build()) + .build(); + +Routing defaultRouting = Routing.builder() + .any((req, res) -> res.send("It works!")) // <5> + .build(); + +WebServer server = WebServer.builder(defaultRouting) + .config(ServerConfiguration.builder() + .port(8080) // <6> + .addSocket("health", SocketConfiguration.builder() // <7> + .port(8081) + .build()) + .build()) + .addNamedRouting("health", healthRouting) // <8> + .build(); + +server.start(); +---- +<1> The health service for the `liveness` probe is exposed at `/live`. +<2> Using the built-in health checks for the `liveness` probe. +<3> The health service for the `readiness` probe is exposed at `/ready`. +<4> Using a custom health check for a pseudo database that is always `UP`. +<5> The default route: returns It works! for any request. +<6> The server uses port 8080 for the default routes. +<7> A socket configuration named `health` using port `8081`. +<8> Route the health services exclusively on the `health` socket. + +[source,yaml] +.Kubernetes descriptor: +---- +kind: Service +apiVersion: v1 +metadata: + name: acme # <1> + labels: + app: acme +spec: + type: NodePort + selector: + app: acme + ports: + - port: 8080 + targetPort: 8080 + name: http +--- +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: acme # <2> +spec: + replicas: 1 + template: + metadata: + name: acme + labels: + name: acme + spec: + containers: + - name: acme + image: acme + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + livenessProbe: + httpGet: + path: /live # <3> + port: 8081 + initialDelaySeconds: 3 # <4> + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /ready # <5> + port: 8081 + initialDelaySeconds: 10 # <6> + periodSeconds: 30 + timeoutSeconds: 10 +--- +---- +<1> A service of type `NodePort` that serves the default routes on port `8080`. +<2> A deployment with one replica of a pod. +<3> The HTTP endpoint for the liveness probe. +<4> The liveness probe configuration. +<5> The HTTP endpoint for the readiness probe. +<6> The readiness probe configuration. + +== Additional Information + +* link:{health-javadoc-base-url}/module-summary.html[Health Checks SE API JavaDocs]. \ No newline at end of file diff --git a/docs/se/health/health-in-k8s.adoc b/docs/se/health/health-in-k8s.adoc deleted file mode 100644 index 2583bbab628..00000000000 --- a/docs/se/health/health-in-k8s.adoc +++ /dev/null @@ -1,235 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Kubernetes Probes -:description: Kubernetes probes -:keywords: helidon, readiness, liveness, probes, kubernetes -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -This document describes how to use the Helidon health check API with Kubernetes. - -== About Kubernetes probes - -Probes is the term used by Kubernetes to describe health checks for containers - (link:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes[Kubernetes documentation]). - -There are three types of probes: - -* _liveness_: Indicates whether the container is running -* _readiness_: Indicates whether the container is ready to service requests -* _startup_: Indicates whether the application in the container has started - -You can implement probes using the following mechanisms: - -. Running a command inside a container -. Sending an `HTTP` request to a container -. Opening a `TCP` socket to a container - -A microservice exposed to HTTP traffic will typically implement both the - liveness probe and the readiness probe using HTTP requests. -If the microservice takes a significant time to initialize itself, you can also define a startup probe, in which case -Kubernetes does not check liveness or readiness probes until the startup probe returns success. - -You can configure several parameters for probes. The following are the most - relevant parameters: - -[cols="2,5",role="flex, sm7"] -|======= -| `initialDelaySeconds` -| Number of seconds after the container has started before liveness or readiness - probes are initiated. - -| `periodSeconds` -| Probe interval. Default to 10 seconds. Minimum value is 1. - -| `timeoutSeconds` -| Number of seconds after which the probe times out. Defaults to 1 second. - Minimum value is 1 - -| `failureThreshold` -| Number of consecutive failures after which the probe should stop. Default: 3. - Minimum: 1. -|======= - -=== Liveness probe - -The liveness probe is used to verify the container has become unresponsive. - For example, it can be used to detect deadlocks or analyze heap usage. When - Kubernetes gives up on a liveness probe, the corresponding pod is restarted. - -NOTE: The liveness probe can result in repeated restarts in certain cases. - For example, if the probe is implemented to check all the dependencies - strictly, then it can fail repeatedly for temporary issues. Repeated restarts - can also occur if `timeoutSeconds` or `periodSeconds` is too low. - -We recommend the following: - -* Avoid checking dependencies in a liveness probe. -* Set `timeoutSeconds` to avoid excessive probe failures. -* Acknowledge startup times with `initialDelaySeconds`. - -=== Readiness probe - -The readiness probe is used to avoid routing requests to the pod until it is - ready to accept traffic. When Kubernetes gives up on a readiness probe, the - pod is not restarted, traffic is not routed to the pod anymore. - -NOTE: In certain cases, the readiness probe can cause all the pods to be removed - from service routing. For example, if the probe is implemented to check all the - dependencies strictly, then it can fail repeatedly for temporary issues. This - issue can also occur if `timeoutSeconds` or `periodSeconds` is too low. - -We recommend the following: - -* Be conservative when checking shared dependencies. -* Be aggressive when checking local dependencies. -* Set `failureThreshold` according to `periodSeconds` in order to accommodate - temporary errors. - -=== Startup probe - -The startup probe prevents Kubernetes from prematurely checking the other probes if the application takes a long time to start. -Otherwise, Kubernetes might misinterpret a failed liveness or readiness probe and shut down the container when, in fact, the application is still coming up. - - -== Troubleshooting probes - -Failed probes are recorded as events associated with their corresponding pods. - The event message contains only the status code. - -[source,bash] -.Get the events of a single pod: ----- -POD_NAME=$(kubectl get pod -l app=acme -o jsonpath='{.items[0].metadata.name}') # <1> -kubectl get event --field-selector involvedObject.name=${POD_NAME} # <2> ----- -<1> Get the effective pod name by filtering pods with the label `app=acme`. -<2> Filter the events for the pod. - -TIP: Create log messages in your health check implementation when setting a - `DOWN` status. This will allow you to correlate the cause of a failed probe. - -== Example - -This example shows the usage of the Helidon health API in an application that - implements health endpoints for the liveness and readiness probes. Note that - the application code dissociates the health endpoints from the default routes, - so that the health endpoints are not exposed by the service. An example YAML - specification is also provided for the Kubernetes service and deployment. - -[source,java] -.Application code: ----- -Routing healthRouting = Routing.builder() - .register(JsonSupport.create()) - .register(HealthSupport.builder() - .webContext("/live") // <1> - .addLiveness(HealthChecks.healthChecks()) // <2> - .build()) - .register(HealthSupport.builder() - .webContext("/ready") // <3> - .addReadiness(() -> HealthCheckResponse.named("database").up().build()) // <4> - .build()) - .build(); - -Routing defaultRouting = Routing.builder() - .any((req, res) -> res.send("It works!")) // <5> - .build(); - -WebServer server = WebServer.builder(defaultRouting) - .config(ServerConfiguration.builder() - .port(8080) // <6> - .addSocket("health", SocketConfiguration.builder() // <7> - .port(8081) - .build()) - .build()) - .addNamedRouting("health", healthRouting) // <8> - .build(); - -server.start(); ----- -<1> The health service for the `liveness` probe is exposed at `/live`. -<2> Using the built-in health checks for the `liveness` probe. -<3> The health service for the `readiness` probe is exposed at `/ready`. -<4> Using a custom health check for a pseudo database that is always `UP`. -<5> The default route: returns It works! for any request. -<6> The server uses port 8080 for the default routes. -<7> A socket configuration named `health` using port `8081`. -<8> Route the health services exclusively on the `health` socket. - -[source,yaml] -.Kubernetes descriptor: ----- -kind: Service -apiVersion: v1 -metadata: - name: acme # <1> - labels: - app: acme -spec: - type: NodePort - selector: - app: acme - ports: - - port: 8080 - targetPort: 8080 - name: http ---- -kind: Deployment -apiVersion: extensions/v1beta1 -metadata: - name: acme # <2> -spec: - replicas: 1 - template: - metadata: - name: acme - labels: - name: acme - spec: - containers: - - name: acme - image: acme - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8080 - livenessProbe: - httpGet: - path: /live # <3> - port: 8081 - initialDelaySeconds: 3 # <4> - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /ready # <5> - port: 8081 - initialDelaySeconds: 10 # <6> - periodSeconds: 30 - timeoutSeconds: 10 ---- ----- -<1> A service of type `NodePort` that serves the default routes on port `8080`. -<2> A deployment with one replica of a pod. -<3> The HTTP endpoint for the liveness probe. -<4> The liveness probe configuration. -<5> The HTTP endpoint for the readiness probe. -<6> The readiness probe configuration. diff --git a/docs/se/health/health.adoc b/docs/se/health/health.adoc deleted file mode 100644 index 16367a9288b..00000000000 --- a/docs/se/health/health.adoc +++ /dev/null @@ -1,279 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Health Checks -:description: Helidon health checks -:keywords: helidon, health checks, health, check -:feature-name: Health Checks -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -This document describes the health check API available with Helidon SE. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.health - helidon-health - ----- - -Optional dependency to use built-in health checks: - -[source,xml] ----- - - io.helidon.health - helidon-health-checks - ----- - -== About health checks - -It’s a good practice to monitor your microservice’s health, to ensure that it is - available and performs correctly. - -Applications implement health checks to expose health status that is collected - at regular intervals by external tooling, such as orchestrators like - Kubernetes. The orchestrator may then take action, such as restarting your - application if the health check fails. - -A typical health check combines the statuses of all the dependencies that - affect availability and the ability to perform correctly: - -* network latency -* storage -* database -* other services used by your application - - -== API overview - -[cols="4,6"] -.Health check API classes -|======= -| `org.eclipse.microprofile.health.HealthCheck` -| Java functional interface representing the logic of a single health check - -| `org.eclipse.microprofile.health.HealthCheckResponse` -| Result of a health check invocation that contains a status and a description. - -| `org.eclipse.microprofile.health.HealthCheckResponseBuilder` -| Builder class to create `HealthCheckResponse` instances - -| `io.helidon.health.HealthSupport` -| WebServer service that exposes `/health` and invokes the registered health - checks - -| `io.helidon.health.HealthSupport.Builder` -| Builder class to create `HealthSupport` instances -|======= - -A health check is a Java functional interface that returns a - `HealthCheckResponse` object. You can choose to implement a health check - inline with a lambda expression or you can reference a method with the double - colon operator `::`. - -[source,java] -.Health check with a lambda expression: ----- -HealthCheck hc = () -> HealthCheckResponse - .named("exampleHealthCheck") - .up() - .build(); ----- - -[source,java] -.Health check with method reference: ----- -HealthCheckResponse exampleHealthCheck(){ - return HealthCheckResponse - .named("exampleHealthCheck") - .up() - .build(); -} -HealthCheck hc = this::exampleHealthCheck; ----- - -`HealthSupport` is a WebServer service that contains a collection of - registered `HealthCheck` instances. When queried, it invokes the registered - health check and returns a response with a status code representing the overall - status of the application. - -[cols="1,5",role="flex, sm7"] -.Health status codes -|======= -| `200` | The application is healthy (with health check details in the response). -| `204` | The application is healthy (with _no_ health check details in the response). -| `503` | The application is not healthy. -| `500` | An error occurred while reporting the health. -|======= - -HTTP `GET` responses include JSON content showing the detailed results of all the health checks which the server executed after receiving the request. -HTTP `HEAD` requests return only the status with no payload. - -[source,java] -.Create the health support service: ----- -HealthSupport health = HealthSupport.builder() - .addLiveness(hc) - .build(); ----- - -[source,java] -.Register a custom health check: ----- -HealthSupport health = HealthSupport.builder() - .addLiveness(() -> HealthCheckResponse.named("exampleHealthCheck") - .up() - .withData("time", System.currentTimeMillis()) - .build()) // <.> - .build(); - -Routing.builder() - .register(health) // <.> - .build(); ----- -<.> Add a custom health check. This example returns `UP` and current time. -<.> Register health support with web server routing (adds the `/health` - endpoint). - -TIP: Balance collecting a lot of information with the need to avoid overloading - the application and overwhelming users. - -[source,json] -.JSON response: ----- -{ - "status": "UP", - "checks": [ - { - "name": "exampleHealthCheck", - "status": "UP", - "data": { - "time": 1546958376613 - } - } - ] -} ----- - -=== Built-in health checks - -You can use Helidon-provided health checks to report various - common health check statuses: - -[[built-in-health-checks-table]] -[cols="1,1,3,15,3"] -|======= -|Built-in health check |Health check name |JavaDoc |Config properties |Default config value - -|deadlock detection -|`deadlock` -| link:{health-javadoc-base-url}/io/helidon/health/checks/DeadlockHealthCheck.html[`DeadlockHealthCheck`] -| n/a -| n/a - -|available disk space -|`diskSpace` -| link:{health-javadoc-base-url}/io/helidon/health/checks/DiskSpaceHealthCheck.html[`DiskSpaceHealthCheck`] -|`helidon.healthCheck.diskSpace.thresholdPercent` + - + -`helidon.healthCheck.diskSpace.path` -| `99.999` + - + -`/` -|available heap memory -| `heapMemory` -| link:{health-javadoc-base-url}/io/helidon/health/checks/HeapMemoryHealthCheck.html[`HeapMemoryHealthCheck`] -|`helidon.healthCheck.heapMemory.thresholdPercent` -|`98` -|======= - -The following code adds the default built-in health checks to your application: - -[source,java] ----- -HealthSupport health = HealthSupport.builder() - .addLiveness(HealthChecks.healthChecks()) // <1> - .build(); - -Routing.builder() - .register(health) // <2> - .build(); ----- -<1> Add built-in health checks using defaults (requires the `helidon-health-checks` - dependency). -<2> Register the created health support with web server routing (adds the -`/health` endpoint). - -You can control the thresholds for built-in health checks in either of two ways: - -* Create the health checks individually -using their builders instead of using the `HealthChecks` convenience class. -Follow the JavaDoc links in the <> above. - -* Configure the behavior of the built-in health checks using the config property keys in the -<>. - -Further, you can suppress one or more of the built-in health checks by setting the configuration item -`helidon.health.exclude` to a comma-separated list of the health check names -(from the <>) you want to exclude. - -== Health report -Accessing the Helidon-provided `/health` endpoint reports the health of your application: -[source,json] -.JSON response. ----- -{ - "status": "UP", - "checks": [ - { - "name": "deadlock", - "status": "UP" - }, - { - "name": "diskSpace", - "status": "UP", - "data": { - "free": "211.00 GB", - "freeBytes": 226563444736, - "percentFree": "45.31%", - "total": "465.72 GB", - "totalBytes": 500068036608 - } - }, - { - "name": "heapMemory", - "status": "UP", - "data": { - "free": "215.15 MB", - "freeBytes": 225600496, - "max": "3.56 GB", - "maxBytes": 3817865216, - "percentFree": "99.17%", - "total": "245.50 MB", - "totalBytes": 257425408 - } - } - ] -} ----- diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index da4f6fe1936..1eb4db2615e 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -83,7 +83,7 @@ Build gRPC servers and clients. //Health Checks [CARD] .Health Checks -[icon=favorite_outline,link=health/health.adoc] +[icon=favorite_outline,link=health.adoc] -- Expose health statuses of your applications. -- diff --git a/docs/se/oci/object-storage.adoc b/docs/se/oci/object-storage.adoc index 74d4e5db9b3..10541575d6d 100644 --- a/docs/se/oci/object-storage.adoc +++ b/docs/se/oci/object-storage.adoc @@ -255,4 +255,4 @@ Routing routing = Routing.builder() ---- When executed, this health check will _ping_ the bucket to make sure it is accessible in your -environment. For more information about health checks see xref:../health/health.adoc[Health Checks]. +environment. For more information about health checks see xref:../health.adoc[Health Checks]. diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 75776e6ca17..9aad71bc261 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -157,15 +157,12 @@ backend: glyph: type: "icon" value: "graphic_eq" - - type: "MENU" + - type: "PAGE" title: "Health Checks" - dir: "health" + source: "health.adoc" glyph: type: "icon" value: "favorite_outline" - sources: - - "health.adoc" - - "health-in-k8s.adoc" - type: "MENU" title: "Metrics" dir: "metrics" From ceb359a401e6c03f336acc19dc4390a915e2b962 Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Wed, 13 Jul 2022 11:32:35 -0400 Subject: [PATCH 06/51] New formatting for GraphQL MP document (#4514) * New formatting for GraphQL MP document. Signed-off-by: Santiago Pericasgeertsen * Set correct GraphQL version. Signed-off-by: Santiago Pericasgeertsen * Minor update in Examples. Signed-off-by: Santiago Pericasgeertsen --- docs/includes/attributes.adoc | 4 + docs/mp/graphql.adoc | 143 ++++++++++++++++------------------ 2 files changed, 69 insertions(+), 78 deletions(-) diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index c422d45f375..edcbfcb0665 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -126,6 +126,10 @@ endif::[] :microprofile-rest-client-spec-url: {microprofile-rest-client-base-url}/microprofile-rest-client-spec-{version-lib-microprofile-rest-client}.html :microprofile-rest-client-javadoc-url: {microprofile-rest-client-base-url}/apidocs +:microprofile-graphql-base-url: {microprofile-base-url}/microprofile-graphql-{version-lib-microprofile-graphql} +:microprofile-graphql-spec-url: {microprofile-rest-graphql-url}/microprofile-rest-client-spec-{version-lib-microprofile-graphql}.html +:microprofile-graphql-javadoc-url: {microprofile-rest-graphql-url}/apidocs + // Jakarta versioned URLs :jakarta-base-url: https://jakarta.ee/specifications diff --git a/docs/mp/graphql.adoc b/docs/mp/graphql.adoc index e48f758dd90..54aeddfc334 100644 --- a/docs/mp/graphql.adoc +++ b/docs/mp/graphql.adoc @@ -25,13 +25,25 @@ include::{rootdir}/includes/mp.adoc[] -The Microprofile GraphQL APIs are an extension to xref:introduction/introduction.adoc[Helidon MP] -to allow building of applications that can expose a GraphQL endpoint. +== ToC -== Experimental +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon MP implements the +link:{microprofile-graphql-spec-url}[MicroProfile GraphQL specification]. +This specifcation describes how applications can be built to expose an endpoint for GraphQL. +GraphQL is an open-source data query and manipulation language for APIs, +and a runtime for fulfilling data queries. +It provides an alternative to, though not necessarily a replacement for, REST. -WARNING: The Helidon GraphQL feature is currently experimental and the APIs are - subject to changes until GraphQL support is stabilized. include::{rootdir}/includes/dependencies.adoc[] @@ -43,28 +55,14 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== About the MicroProfile GraphQL Specification -Helidon MP implements the MicroProfile GraphQL -link:https://github.com/eclipse/microprofile-graphql[spec] version {version-lib-microprofile-graphql}. -The spec prescribes how applications can be built to expose an endpoint for GraphQL. -GraphQL is an open-source data query and manipulation language for APIs, -and a runtime for fulfilling queries with existing data. -It provides an alternative to, though not necessarily a replacement for, REST. - -For more information on GraphQL see https://graphql.org/. - -== Getting Started - -=== Defining your API +== API The MicroProfile GraphQL specification defines a number of key annotations to be used when writing a GraphQL endpoint: -* `@GraphQLApi` - identifies a CDI Bean as a GraphQL Endpoint -* `@Query` - identifies a method as returning specified fields for an object or collection of entities +* `@GraphQLApi` - identifies a CDI Bean as a GraphQL endpoint +* `@Query` - identifies a method as returning one or more entities * `@Mutation` - identifies a method which creates, deletes or updates entities -NOTE: Please see the link:https://github.com/eclipse/microprofile-graphql[Microprofile GraphQL spec] for the full list of available annotations. - For example, the following defines a GraphQL endpoint with a number of queries and mutations that work against a fictional `CustomerService` service and `Customer` class. @@ -72,28 +70,28 @@ against a fictional `CustomerService` service and `Customer` class. .Simple ContactGraphQLApi ---- @ApplicationScoped -@org.eclipse.microprofile.graphql.GraphQLApi +@GraphQLApi public class ContactGraphQLApi { @Inject private CustomerService customerService; - @org.eclipse.microprofile.graphql.Query + @Query public Collection findAllCustomers() { <1> return customerService.getAllCustomers(); } - @org.eclipse.microprofile.graphql.Query + @Query public Customer findCustomer(@Name("customerId") int id) { <2> return customerService.getCustomer(id); } - @org.eclipse.microprofile.graphql.Query + @Query public Collection findCustomersByName(@Name("name") String name) { <3> return customerService.getAllCustomers(name); } - @org.eclipse.microprofile.graphql.Mutation + @Mutation public Contact createCustomer(@Name("customerId") int id, <4> @Name("name") String name, @Name("balance") float balance) { @@ -111,14 +109,14 @@ public class customer { } ---- -<1> a query with no-arguments that will return all Customers -<2> a query that takes an argument to return a specific Customer -<3> a query that optionally takes a name and returns a collection of Customers -<4> a mutation that creates a Customer and returns the newly created Customer +<1> a query with no-arguments that will return all `Customer` s +<2> a query that takes an argument to return a specific `Customer` +<3> a query that optionally takes a name and returns a collection of `Customer` s +<4> a mutation that creates a Customer and returns the newly created `Customer` -The above would generate a GraphQL schema as shown below: +The example above would generate a GraphQL schema as shown below: [source,graphql] -.Sample GraphQL Schema +.Sample GraphQL schema ---- type Query { findAllCustomers: [Customer] @@ -140,66 +138,41 @@ type Customer { After application startup, a GraphQL schema will be generated from your annotated API classes and POJO's and you will be able to access these via the URLs described below. -=== Creating your entry-point - -As per the instructions xref:introduction/microprofile.adoc[here] ensure you have added a -`src/main/resources/META-INF/beans.xml` file, so the CDI implementation can pick up your classes. - -A `Main` class is not needed, you can configure `io.helidon.microprofile.cdi.Main` as the entry point. - -Optionally, you can configure a custom entry point (such as when you need custom configuration setup). - -[source,java] -.Sample Entry-point ----- -public class MyMain { - public static void main(String[] args) { - io.helidon.microprofile.cdi.Main.main(args); - } -} ----- - === Building your application As part of building your application, you must create a Jandex index -using the `jandex-maven-plugin` for all API and POJO classes that are used. +using the `jandex-maven-plugin` for all API and POJO classes. [source,xml] .Generate Jandex index ---- -org.jboss.jandex -jandex-maven-plugin - - - make-index - - + org.jboss.jandex + jandex-maven-plugin + + + make-index + + ---- -== Accessing the GraphQL end-points - -After starting your application you should see a message similar to the following indicating the GraphQL support is available: - -[source,bash] -.Sample Startup output ----- -2020.11.16 12:29:58 INFO io.helidon.common.HelidonFeatures Thread[features-thread,5,main]: Helidon MP 2.1.1-SNAPSHOT features: [CDI, Config, Fault Tolerance, GraphQL, Health, JAX-RS, Metrics, Open API, Security, Server, Tracing] -2020.11.16 12:29:58 INFO io.helidon.common.HelidonFeatures.experimental Thread[features-thread,5,main]: You are using experimental features. These APIs may change, please follow changelog! -2020.11.16 12:29:58 INFO io.helidon.common.HelidonFeatures.experimental Thread[features-thread,5,main]: Experimental feature: GraphQL (GraphQL) ----- +NOTE: As per the instructions xref:introduction/microprofile.adoc[here] ensure you have added a +`src/main/resources/META-INF/beans.xml` file, so the CDI implementation can pick up your classes. -You can then use your GraphQL client via the default endpoint `http://host:port/graphql`. +=== Accessing the GraphQL endpoints -The GraphQL Schema is available via `http://host:port/graphql/schema.graphql`. +After starting your application you should see a log message indicating that GraphQL +is in the list of features. You can access the GraphQL endpoint at `http://host:port/graphql`, and +the corresponding schema at `http://host:port/graphql/schema.graphql`. See <> for +additional information on how to change the location of these resources. -NOTE: If you wish to use the GraphQL UI (https://github.com/graphql/graphiql) then please see the Helidon Microprofile GraphQL -link:{helidon-github-tree-url}/master/examples/microprofile/graphql[example]. +If you wish to use the +link:https://github.com/graphql/graphiql[GraphQL UI] then please see the +link:{helidon-github-tree-url}/master/examples/microprofile/graphql[GraphQL MP Example]. -== Configuration Options +== Configuration -=== MicroProfile GraphQL The specification defines the following configuration options: [cols="2,2,5"] @@ -214,6 +187,20 @@ The specification defines the following configuration options: |=== These configuration options are more significant that the configuration options - that can be used to configure GraphQL invocation (see below). +that can be used to configure GraphQL invocation (see below). include::{rootdir}/includes/graphql.adoc[] + +== Examples + +For a complete example, see +link:{helidon-github-tree-url}/master/examples/microprofile/graphql[GraphQL MP Example]. + +== Additional Information + + * link:http://graphql.org[GraphQL]. + +== Reference + + * link:{microprofile-graphql-javadoc-url}[MicroProfile GraphQL Javadocs]. + From a9d721b1fdc314b6a81ed338ae7c71885ebcd5d0 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 13 Jul 2022 12:56:20 -0700 Subject: [PATCH 07/51] Updates OCI extension project to include BouncyCastle in test scope only (#4531) Signed-off-by: Laird Nelson --- integrations/oci/sdk/cdi/pom.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/integrations/oci/sdk/cdi/pom.xml b/integrations/oci/sdk/cdi/pom.xml index 3e5d353cedc..c6a8e5e8dd2 100644 --- a/integrations/oci/sdk/cdi/pom.xml +++ b/integrations/oci/sdk/cdi/pom.xml @@ -85,16 +85,6 @@ runtime true - - - org.bouncycastle - bcpkix-jdk15on - runtime - org.jboss jandex @@ -134,6 +124,16 @@ helidon-microprofile-tests-junit5 test + + + org.bouncycastle + bcpkix-jdk15on + test + org.slf4j slf4j-jdk14 From 15a81cd27d25570577ab5756190fae77d6976fd7 Mon Sep 17 00:00:00 2001 From: Arjav Desai Date: Wed, 13 Jul 2022 17:33:25 -0500 Subject: [PATCH 08/51] OCI Integration Examples Update and Deprecation 3.x (#4516) --- bom/pom.xml | 45 -- .../io/helidon/common/FeatureCatalog.java | 27 +- dependencies/pom.xml | 25 - docs/mp/extensions/cdi-oci-objectstorage.adoc | 42 -- docs/mp/extensions/overview.adoc | 8 - docs/se/oci/atp.adoc | 2 +- docs/se/oci/object-storage.adoc | 2 +- docs/se/oci/oci.adoc | 3 + docs/sitegen.yaml | 1 - .../cdi/oci-objectstorage/.dockerignore | 1 - .../cdi/oci-objectstorage/.gitignore | 1 - .../cdi/oci-objectstorage/Dockerfile | 52 -- .../cdi/oci-objectstorage/README.md | 75 -- .../cdi/oci-objectstorage/app.yaml | 107 --- .../cdi/oci-objectstorage/oci-setup.sh | 97 --- .../cdi/oci-objectstorage/pom.xml | 98 --- .../jaxrs/HelidonLogoResource.java | 126 ---- .../oci/objectstorage/jaxrs/package-info.java | 21 - .../src/main/resources/META-INF/beans.xml | 25 - .../META-INF/microprofile-config.properties | 22 - .../reflect-config.json | 8 - .../native-image.properties | 17 - .../commons-logging/reflect-config.json | 26 - examples/integrations/cdi/pom.xml | 1 - examples/integrations/oci/atp-cdi/README.md | 2 +- examples/integrations/oci/atp-cdi/pom.xml | 34 +- .../integrations/oci/atp/cdi/AtpResource.java | 134 +++- .../atp-cdi/src/main/java/module-info.java | 9 +- .../helidon/serial-config.properties} | 4 +- .../src/main/resources/application.yaml | 4 +- .../integrations/oci/atp-reactive/README.md | 2 +- .../integrations/oci/atp-reactive/pom.xml | 29 +- .../oci/atp/reactive/AtpService.java | 154 +++- .../oci/atp/reactive/OciAtpMain.java | 25 +- .../oci/atp/reactive/OciResponseHandler.java | 50 ++ .../src/main/java/module-info.java | 7 +- .../helidon/serial-config.properties} | 3 +- .../src/main/resources/application.yaml | 4 +- .../integrations/oci/metrics-reactive/pom.xml | 33 +- .../telemetry/reactive/OciMetricsMain.java | 150 ++-- .../src/main/java/module-info.java | 5 +- .../oci/objectstorage-cdi/pom.xml | 34 +- .../cdi/ObjectStorageResource.java | 151 ++-- .../src/main/java/module-info.java | 13 +- .../src/main/resources/application.yaml | 5 +- .../oci/objectstorage-reactive/frank.png | Bin 10516 -> 0 bytes .../oci/objectstorage-reactive/pom.xml | 33 +- .../reactive/ObjectStorageService.java | 219 ++++-- .../reactive/OciObjectStorageMain.java | 35 +- .../src/main/java/module-info.java | 9 +- .../src/main/resources/application.yaml | 5 +- examples/integrations/oci/pom.xml | 22 +- examples/integrations/oci/vault-cdi/pom.xml | 49 +- .../oci/vault/cdi/CryptoClientProducer.java | 45 ++ .../oci/vault/cdi/ErrorHandlerProvider.java | 37 + .../oci/vault/cdi/VaultResource.java | 180 +++-- .../vault-cdi/src/main/java/module-info.java | 7 +- .../src/main/resources/application.yaml | 21 +- .../integrations/oci/vault-reactive/pom.xml | 33 +- .../oci/vault/reactive/OciHandler.java | 51 ++ .../oci/vault/reactive/OciVaultMain.java | 45 +- .../oci/vault/reactive/VaultService.java | 184 +++-- .../src/main/java/module-info.java | 9 +- .../src/main/resources/application.yaml | 10 +- examples/security/vaults/pom.xml | 4 - .../ucp/cdi/UCPBackedDataSourceExtension.java | 4 + .../cdi/oci-objectstorage-cdi/README.md | 65 -- .../etc/spotbugs/exclude.xml | 32 - .../cdi/oci-objectstorage-cdi/pom.xml | 117 --- ...leConfigAuthenticationDetailsProvider.java | 105 --- .../OCIObjectStorageExtension.java | 350 --------- .../objectstorage/OciConfigConfigSource.java | 213 ------ .../cdi/oci/objectstorage/package-info.java | 24 - .../src/main/java/module-info.java | 37 - .../TestOCIObjectStorageExtension.java | 80 --- .../src/test/resources/META-INF/beans.xml | 25 - integrations/cdi/pom.xml | 1 - integrations/oci/atp/pom.xml | 66 -- .../atp/GenerateAutonomousDatabaseWallet.java | 172 ----- .../integrations/oci/atp/OciAutonomousDb.java | 47 -- .../oci/atp/OciAutonomousDbImpl.java | 33 - .../atp/OciAutonomousDbInjectionProvider.java | 63 -- .../oci/atp/OciAutonomousDbRx.java | 210 ------ .../oci/atp/OciAutonomousDbRxImpl.java | 60 -- .../integrations/oci/atp/package-info.java | 23 - .../oci/atp/src/main/java/module-info.java | 46 -- .../helidon/native-image/weld-proxies.json | 14 - integrations/oci/cdi/pom.xml | 47 -- .../integrations/oci/cdi/OciCdiExtension.java | 199 ------ .../integrations/oci/cdi/OciInternal.java | 76 -- .../integrations/oci/cdi/QualifiedBean.java | 125 ---- .../integrations/oci/cdi/package-info.java | 20 - .../oci/cdi/src/main/java/module-info.java | 39 - .../helidon/native-image/weld-proxies.json | 8 - .../META-INF/native-image/reflect-config.json | 7 - .../oci/connect/etc/spotbugs/exclude.xml | 31 - integrations/oci/connect/pom.xml | 103 --- .../oci/connect/OciApiException.java | 62 -- .../connect/OciConfigInstancePrincipal.java | 577 --------------- .../oci/connect/OciConfigProfile.java | 520 -------------- .../oci/connect/OciConfigProvider.java | 68 -- .../connect/OciConfigResourcePrincipal.java | 82 --- .../oci/connect/OciHttpSignature.java | 203 ------ .../connect/OciOutboundSecurityProvider.java | 165 ----- .../oci/connect/OciRequestBase.java | 135 ---- .../oci/connect/OciResponseParser.java | 62 -- .../integrations/oci/connect/OciRestApi.java | 535 -------------- .../oci/connect/OciRestException.java | 101 --- .../oci/connect/OciSignatureData.java | 64 -- .../oci/connect/RsaSessionKeys.java | 60 -- .../integrations/oci/connect/SessionKeys.java | 47 -- .../oci/connect/package-info.java | 20 - .../oci/connect/spi/InjectionProvider.java | 97 --- .../oci/connect/spi/package-info.java | 20 - .../connect/src/main/java/module-info.java | 47 -- integrations/oci/objectstorage-health/pom.xml | 84 --- .../health/OciObjectStorageHealthCheck.java | 192 ----- .../objectstorage/health/package-info.java | 20 - .../src/main/java/module-info.java | 40 -- .../src/main/resources/META-INF/beans.xml | 25 - .../OciObjectStorageHealthCheckTest.java | 30 - .../src/test/resources/application.yaml | 24 - integrations/oci/objectstorage/pom.xml | 76 -- .../oci/objectstorage/DeleteObject.java | 80 --- .../oci/objectstorage/GetBucket.java | 215 ------ .../oci/objectstorage/GetObject.java | 89 --- .../oci/objectstorage/GetObjectRx.java | 65 -- .../oci/objectstorage/ObjectRequest.java | 102 --- .../oci/objectstorage/OciObjectStorage.java | 82 --- .../objectstorage/OciObjectStorageImpl.java | 61 -- .../OciObjectStorageInjectionProvider.java | 64 -- .../oci/objectstorage/OciObjectStorageRx.java | 228 ------ .../objectstorage/OciObjectStorageRxImpl.java | 123 ---- .../oci/objectstorage/PutObject.java | 107 --- .../oci/objectstorage/RenameObject.java | 119 ---- .../oci/objectstorage/package-info.java | 23 - .../src/main/java/module-info.java | 40 -- .../helidon/native-image/weld-proxies.json | 14 - integrations/oci/pom.xml | 8 - integrations/oci/telemetry/pom.xml | 51 -- .../oci/telemetry/OciMetrics.java | 53 -- .../oci/telemetry/OciMetricsImpl.java | 30 - .../oci/telemetry/OciMetricsRx.java | 205 ------ .../oci/telemetry/OciMetricsRxImpl.java | 61 -- .../OciTelemetryInjectionProvider.java | 62 -- .../oci/telemetry/PostMetricData.java | 368 ---------- .../oci/telemetry/package-info.java | 23 - .../telemetry/src/main/java/module-info.java | 39 - .../helidon/native-image/weld-proxies.json | 14 - integrations/oci/vault-health/pom.xml | 84 --- .../oci/vault/health/OciVaultHealthCheck.java | 168 ----- .../oci/vault/health/package-info.java | 20 - .../src/main/java/module-info.java | 37 - .../src/main/resources/META-INF/beans.xml | 25 - integrations/oci/vault/pom.xml | 63 -- .../integrations/oci/vault/CreateSecret.java | 234 ------ .../integrations/oci/vault/Decrypt.java | 206 ------ .../integrations/oci/vault/DeleteSecret.java | 121 ---- .../integrations/oci/vault/Encrypt.java | 209 ------ .../integrations/oci/vault/GetKey.java | 182 ----- .../integrations/oci/vault/GetSecret.java | 80 --- .../oci/vault/GetSecretBundle.java | 252 ------- .../integrations/oci/vault/GetVault.java | 213 ------ .../integrations/oci/vault/OciVault.java | 117 --- .../integrations/oci/vault/OciVaultImpl.java | 77 -- .../oci/vault/OciVaultInjectionProvider.java | 64 -- .../integrations/oci/vault/OciVaultRx.java | 451 ------------ .../oci/vault/OciVaultRxImpl.java | 229 ------ .../oci/vault/OciVaultSecurityProvider.java | 672 ------------------ .../oci/vault/OciVaultSecurityService.java | 53 -- .../integrations/oci/vault/Secret.java | 172 ----- .../integrations/oci/vault/SecretStage.java | 43 -- .../helidon/integrations/oci/vault/Sign.java | 250 ------- .../integrations/oci/vault/Verify.java | 172 ----- .../integrations/oci/vault/package-info.java | 20 - .../oci/vault/src/main/java/module-info.java | 38 - .../helidon/native-image/weld-proxies.json | 14 - parent/pom.xml | 25 + pom.xml | 2 +- 179 files changed, 1372 insertions(+), 13727 deletions(-) delete mode 100644 docs/mp/extensions/cdi-oci-objectstorage.adoc delete mode 100644 examples/integrations/cdi/oci-objectstorage/.dockerignore delete mode 100644 examples/integrations/cdi/oci-objectstorage/.gitignore delete mode 100644 examples/integrations/cdi/oci-objectstorage/Dockerfile delete mode 100644 examples/integrations/cdi/oci-objectstorage/README.md delete mode 100644 examples/integrations/cdi/oci-objectstorage/app.yaml delete mode 100644 examples/integrations/cdi/oci-objectstorage/oci-setup.sh delete mode 100644 examples/integrations/cdi/oci-objectstorage/pom.xml delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/HelidonLogoResource.java delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/package-info.java delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/beans.xml delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/microprofile-config.properties delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/reflect-config.json delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.oracle.oci.sdk/oci-java-sdk-common/native-image.properties delete mode 100644 examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/commons-logging/commons-logging/reflect-config.json rename examples/integrations/{cdi/oci-objectstorage/src/main/resources/META-INF/native-image/org.apache.httpcomponents/httpclient/native-image.properties => oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties} (89%) create mode 100644 examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciResponseHandler.java rename examples/integrations/{cdi/oci-objectstorage/src/main/resources/commons-logging.properties => oci/atp-reactive/src/main/resources/META-INF/helidon/serial-config.properties} (88%) delete mode 100644 examples/integrations/oci/objectstorage-reactive/frank.png create mode 100644 examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/CryptoClientProducer.java create mode 100644 examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/ErrorHandlerProvider.java create mode 100644 examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciHandler.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/README.md delete mode 100644 integrations/cdi/oci-objectstorage-cdi/etc/spotbugs/exclude.xml delete mode 100644 integrations/cdi/oci-objectstorage-cdi/pom.xml delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/MicroProfileConfigAuthenticationDetailsProvider.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OCIObjectStorageExtension.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OciConfigConfigSource.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/package-info.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/main/java/module-info.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/test/java/io/helidon/integrations/cdi/oci/objectstorage/TestOCIObjectStorageExtension.java delete mode 100644 integrations/cdi/oci-objectstorage-cdi/src/test/resources/META-INF/beans.xml delete mode 100644 integrations/oci/atp/pom.xml delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/GenerateAutonomousDatabaseWallet.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDb.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbImpl.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbInjectionProvider.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRx.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRxImpl.java delete mode 100644 integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/package-info.java delete mode 100644 integrations/oci/atp/src/main/java/module-info.java delete mode 100644 integrations/oci/atp/src/main/resources/META-INF/helidon/native-image/weld-proxies.json delete mode 100644 integrations/oci/cdi/pom.xml delete mode 100644 integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciCdiExtension.java delete mode 100644 integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciInternal.java delete mode 100644 integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/QualifiedBean.java delete mode 100644 integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/package-info.java delete mode 100644 integrations/oci/cdi/src/main/java/module-info.java delete mode 100644 integrations/oci/cdi/src/main/resources/META-INF/helidon/native-image/weld-proxies.json delete mode 100644 integrations/oci/cdi/src/main/resources/META-INF/native-image/reflect-config.json delete mode 100644 integrations/oci/connect/etc/spotbugs/exclude.xml delete mode 100644 integrations/oci/connect/pom.xml delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciApiException.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigInstancePrincipal.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProfile.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProvider.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigResourcePrincipal.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciHttpSignature.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciOutboundSecurityProvider.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRequestBase.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciResponseParser.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestApi.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestException.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciSignatureData.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/RsaSessionKeys.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/SessionKeys.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/package-info.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/InjectionProvider.java delete mode 100644 integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/package-info.java delete mode 100644 integrations/oci/connect/src/main/java/module-info.java delete mode 100644 integrations/oci/objectstorage-health/pom.xml delete mode 100644 integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheck.java delete mode 100644 integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/package-info.java delete mode 100644 integrations/oci/objectstorage-health/src/main/java/module-info.java delete mode 100644 integrations/oci/objectstorage-health/src/main/resources/META-INF/beans.xml delete mode 100644 integrations/oci/objectstorage-health/src/test/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheckTest.java delete mode 100644 integrations/oci/objectstorage-health/src/test/resources/application.yaml delete mode 100644 integrations/oci/objectstorage/pom.xml delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/DeleteObject.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetBucket.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObject.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObjectRx.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/ObjectRequest.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorage.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageImpl.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageInjectionProvider.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRx.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRxImpl.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/PutObject.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/RenameObject.java delete mode 100644 integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/package-info.java delete mode 100644 integrations/oci/objectstorage/src/main/java/module-info.java delete mode 100644 integrations/oci/objectstorage/src/main/resources/META-INF/helidon/native-image/weld-proxies.json delete mode 100644 integrations/oci/telemetry/pom.xml delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetrics.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsImpl.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRx.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRxImpl.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciTelemetryInjectionProvider.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/PostMetricData.java delete mode 100644 integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/package-info.java delete mode 100644 integrations/oci/telemetry/src/main/java/module-info.java delete mode 100644 integrations/oci/telemetry/src/main/resources/META-INF/helidon/native-image/weld-proxies.json delete mode 100644 integrations/oci/vault-health/pom.xml delete mode 100644 integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/OciVaultHealthCheck.java delete mode 100644 integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/package-info.java delete mode 100644 integrations/oci/vault-health/src/main/java/module-info.java delete mode 100644 integrations/oci/vault-health/src/main/resources/META-INF/beans.xml delete mode 100644 integrations/oci/vault/pom.xml delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/CreateSecret.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Decrypt.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/DeleteSecret.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Encrypt.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetKey.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecret.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecretBundle.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetVault.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVault.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultImpl.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultInjectionProvider.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRx.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRxImpl.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityProvider.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityService.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Secret.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/SecretStage.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Sign.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Verify.java delete mode 100644 integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/package-info.java delete mode 100644 integrations/oci/vault/src/main/java/module-info.java delete mode 100644 integrations/oci/vault/src/main/resources/META-INF/helidon/native-image/weld-proxies.json diff --git a/bom/pom.xml b/bom/pom.xml index 78983a1c576..65b35cb47bb 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -928,11 +928,6 @@ helidon-integrations-cdi-jta-weld ${helidon.version} - - io.helidon.integrations.cdi - helidon-integrations-cdi-oci-objectstorage - ${helidon.version} - io.helidon.integrations.graal helidon-graal-native-image-extension @@ -968,51 +963,11 @@ helidon-integrations-micrometer-cdi ${helidon.version} - - io.helidon.integrations.oci - helidon-integrations-oci-atp - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-cdi - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage-health - ${helidon.version} - io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-cdi ${helidon.version} - - io.helidon.integrations.oci - helidon-integrations-oci-telemetry - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-vault - ${helidon.version} - - - io.helidon.integrations.oci - helidon-integrations-oci-vault-health - ${helidon.version} - io.helidon.integrations.vault helidon-integrations-vault diff --git a/common/common/src/main/java/io/helidon/common/FeatureCatalog.java b/common/common/src/main/java/io/helidon/common/FeatureCatalog.java index 40114fbeba8..82987967fd8 100644 --- a/common/common/src/main/java/io/helidon/common/FeatureCatalog.java +++ b/common/common/src/main/java/io/helidon/common/FeatureCatalog.java @@ -179,13 +179,6 @@ final class FeatureCatalog { .experimental(true) .nativeSupported(true) .flavor(HelidonFlavor.SE)); - add("io.helidon.integrations.oci.connect", - FeatureDescriptor.builder() - .name("OCI") - .description("OCI Integration") - .path("OCI") - .flavor(HelidonFlavor.SE) - .experimental(true)); add("io.helidon.integrations.vault", FeatureDescriptor.builder() .name("HCP Vault") @@ -377,11 +370,11 @@ final class FeatureCatalog { .nativeSupported(true) .flavor(HelidonFlavor.MP)); - add("io.helidon.integrations.oci.cdi", + add("io.helidon.integrations.oci.sdk.cdi", FeatureDescriptor.builder() - .name("OCI") - .description("OCI Integration") - .path("OCI") + .name("OCI SDK") + .description("OCI SDK Integration") + .path("OCI SDK") .flavor(HelidonFlavor.MP)); add("io.helidon.integrations.vault.cdi", @@ -625,18 +618,6 @@ final class FeatureCatalog { "Static Content", "Static content support for webserver", "WebServer", "Static Content"); - add("io.helidon.integrations.oci.objectstorage", - "OCI Object Storage", - "Integration with OCI Object Storage", - "OCI", "Object Storage"); - add("io.helidon.integrations.oci.vault", - "OCI Vault", - "Integration with OCI Vault", - "OCI", "Vault"); - add("io.helidon.integrations.oci.telemetry", - "OCI Telemetry", - "Integration with OCI Telemetry", - "OCI", "Telemetry"); add("io.helidon.integrations.vault.auths.approle", "AppRole", "AppRole Authentication Method", diff --git a/dependencies/pom.xml b/dependencies/pom.xml index fa61e11b73d..30ba34fd28e 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -42,8 +42,6 @@ 1.0.0 1.11.20 1.0.3 - - 1.15 1.2 9.1.6 4.1.2 @@ -128,7 +126,6 @@ 4.1.77.Final 0.0.8.Final 2.35.0 - ${version.lib.oci} 21.3.0.0 19.3.0.0 3.14.9 @@ -858,18 +855,6 @@ postgresql ${version.lib.postgresql} - - com.oracle.oci.sdk - oci-java-sdk-objectstorage - ${version.lib.oci-java-sdk-objectstorage} - pom - - - org.glassfish.hk2.external - jakarta.inject - - - com.zaxxer HikariCP @@ -1312,16 +1297,6 @@ groovy-all ${version.lib.groovy-all} - - - commons-codec - commons-codec - ${version.lib.commons-codec} - - - 4.0.0 - - io.helidon.applications - helidon-mp - 3.0.0-SNAPSHOT - ../../../../applications/mp/pom.xml - - io.helidon.examples.integrations.cdi - helidon-examples-integrations-cdi-oci-objectstorage - Helidon CDI Extensions Examples OCI ObjectStorage - - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - compile - - - com.oracle.oci.sdk - oci-java-sdk-objectstorage - compile - pom - - - org.eclipse.microprofile.config - microprofile-config-api - compile - - - - - io.helidon.integrations.oci.sdk - helidon-integrations-oci-sdk-cdi - runtime - - - org.jboss - jandex - runtime - true - - - io.helidon.microprofile.server - helidon-microprofile-server - runtime - - - io.helidon.microprofile.config - helidon-microprofile-config - runtime - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-libs - - - - - org.jboss.jandex - jandex-maven-plugin - - - make-index - - - - - - diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/HelidonLogoResource.java b/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/HelidonLogoResource.java deleted file mode 100644 index 84aae649087..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/HelidonLogoResource.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ -package io.helidon.integrations.examples.oci.objectstorage.jaxrs; - -import java.util.Objects; - -import com.oracle.bmc.model.BmcException; -import com.oracle.bmc.objectstorage.ObjectStorage; -import com.oracle.bmc.objectstorage.requests.GetObjectRequest; -import com.oracle.bmc.objectstorage.responses.GetObjectResponse; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import org.eclipse.microprofile.config.inject.ConfigProperty; - -/** - * A JAX-RS resource class rooted at {@code /logo}. - * - * @see #getLogo(String, String, String) - */ -@Path("/logo") -@ApplicationScoped -public class HelidonLogoResource { - - private final ObjectStorage client; - - private final String namespaceName; - - /** - * Creates a new {@link HelidonLogoResource}. - * - * @param client an {@link ObjectStorage} client; must not be {@code - * null} - * - * @param namespaceName the name of an OCI object storage namespace that will be used; must not be {@code null} - * - * @exception NullPointerException if either parameter is {@code - * null} - */ - @Inject - public HelidonLogoResource(final ObjectStorage client, - @ConfigProperty(name = "oci.objectstorage.namespace") final String namespaceName) { - super(); - this.client = Objects.requireNonNull(client); - this.namespaceName = Objects.requireNonNull(namespaceName); - } - - /** - * Returns a non-{@code null} {@link Response} which, if successful, will contain the object stored under the supplied {@code - * namespaceName}, {@code bucketName} and {@code objectName}. - * - * @param namespaceName the OCI object storage namespace to use; must not be {@code null} - * - * @param bucketName the OCI object storage bucket name to use; must not be {@code null} - * - * @param objectName the OCI object storage object name to use; must not be {@code null} - * - * @return a non-{@code null} {@link Response} describing the operation - * - * @exception NullPointerException if any of the parameters is {@code null} - */ - @GET - @Path("/{namespaceName}/{bucketName}/{objectName}") - @Produces(MediaType.WILDCARD) - public Response getLogo(@PathParam("namespaceName") String namespaceName, - @PathParam("bucketName") final String bucketName, - @PathParam("objectName") final String objectName) { - final Response returnValue; - if (bucketName == null || bucketName.isEmpty() || objectName == null || objectName.isEmpty()) { - returnValue = Response.status(400) - .build(); - } else { - if (namespaceName == null || namespaceName.isEmpty()) { - namespaceName = this.namespaceName; - } - Response temp = null; - try { - final GetObjectRequest request = GetObjectRequest.builder() - .namespaceName(namespaceName) - .bucketName(bucketName) - .objectName(objectName) - .build(); - assert request != null; - final GetObjectResponse response = this.client.getObject(request); - assert response != null; - final Long contentLength = response.getContentLength(); - assert contentLength != null; - if (contentLength <= 0L) { - temp = Response.noContent() - .build(); - } else { - temp = Response.ok() - .type(response.getContentType()) - .entity(response.getInputStream()) - .build(); - } - } catch (final BmcException bmcException) { - final int statusCode = bmcException.getStatusCode(); - temp = Response.status(statusCode) - .build(); - } finally { - returnValue = temp; - } - } - return returnValue; - } - -} diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/package-info.java b/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/package-info.java deleted file mode 100644 index 419f8a83c14..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/java/io/helidon/integrations/examples/oci/objectstorage/jaxrs/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -/** - * Provides JAX-RS-related classes and interfaces for this example - * project. - */ -package io.helidon.integrations.examples.oci.objectstorage.jaxrs; diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/beans.xml b/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/beans.xml deleted file mode 100644 index 1b3fbc297cb..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/microprofile-config.properties b/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/microprofile-config.properties deleted file mode 100644 index 6334cc21f5e..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/microprofile-config.properties +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2018 Oracle and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT 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.objectstorage.namespace=${oci.objectstorage.namespaceName} - - -# Microprofile server properties -server.port=8080 -server.host=0.0.0.0 diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/reflect-config.json b/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/reflect-config.json deleted file mode 100644 index 8c7bac5853f..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/reflect-config.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "name": "com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector", - "methods": [ - { "name": "", "parameterTypes": [] } - ] - } -] diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.oracle.oci.sdk/oci-java-sdk-common/native-image.properties b/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.oracle.oci.sdk/oci-java-sdk-common/native-image.properties deleted file mode 100644 index 8e75fe31d43..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/com.oracle.oci.sdk/oci-java-sdk-common/native-image.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. -# -Args=\ ---initialize-at-run-time=com.oracle.bmc.auth.AbstractFederationClientAuthenticationDetailsProviderBuilder$SessionKeySupplierImpl diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/commons-logging/commons-logging/reflect-config.json b/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/commons-logging/commons-logging/reflect-config.json deleted file mode 100644 index ab7ae619e9e..00000000000 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/commons-logging/commons-logging/reflect-config.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "name": "org.apache.commons.logging.impl.Jdk14Logger", - "methods": [ - { "name": "", "parameterTypes": [ "java.lang.String" ] } - ] - }, - { - "name": "org.apache.commons.logging.impl.LogFactoryImpl", - "methods": [ - { "name": "", "parameterTypes": [] } - ] - }, - { - "name": "org.apache.commons.logging.impl.NoOpLog", - "methods": [ - { "name": "", "parameterTypes": [ "java.lang.String" ] } - ] - }, - { - "name": "org.apache.commons.logging.impl.SimpleLog", - "methods": [ - { "name": "", "parameterTypes": [ "java.lang.String" ] } - ] - } -] diff --git a/examples/integrations/cdi/pom.xml b/examples/integrations/cdi/pom.xml index 99d1242b08d..789709e5189 100644 --- a/examples/integrations/cdi/pom.xml +++ b/examples/integrations/cdi/pom.xml @@ -37,7 +37,6 @@ datasource-hikaricp-mysql jedis jpa - oci-objectstorage pokemons diff --git a/examples/integrations/oci/atp-cdi/README.md b/examples/integrations/oci/atp-cdi/README.md index 64fc9cd668c..33e8176703c 100644 --- a/examples/integrations/oci/atp-cdi/README.md +++ b/examples/integrations/oci/atp-cdi/README.md @@ -8,7 +8,7 @@ Before running the test, make sure to update required properties in `application - oci.atp.ocid: This is OCID of your running ATP instance. - oci.atp.walletPassword: password to encrypt the keys inside the wallet. The password must be at least 8 characters long and must include at least 1 letter and either 1 numeric character or 1 special character. -- oracle.ucp.jdbc.PoolDataSource.atp.serviceName: serviceName of your database running inside OCI ATP. +- oracle.ucp.jdbc.PoolDataSource.atp.tnsNetServiceName: netServiceName of your database running inside OCI ATP as can be found in `tnsnames.ora` file. - oracle.ucp.jdbc.PoolDataSource.atp.user: User to access your database running inside OCI ATP. - oracle.ucp.jdbc.PoolDataSource.atp.password: Password of user to access your database running inside OCI ATP. diff --git a/examples/integrations/oci/atp-cdi/pom.xml b/examples/integrations/oci/atp-cdi/pom.xml index 2f1c30cf825..46e5a252db3 100644 --- a/examples/integrations/oci/atp-cdi/pom.xml +++ b/examples/integrations/oci/atp-cdi/pom.xml @@ -33,6 +33,21 @@ Helidon Examples Integration OCI ATP CDI CDI integration with OCI ATP. + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.microprofile.bundles @@ -48,12 +63,23 @@ pom - io.helidon.integrations.oci - helidon-integrations-oci-cdi + io.helidon.integrations.oci.sdk + helidon-integrations-oci-sdk-cdi + runtime + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full - io.helidon.integrations.oci - helidon-integrations-oci-atp + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime io.helidon.config diff --git a/examples/integrations/oci/atp-cdi/src/main/java/io/helidon/examples/integrations/oci/atp/cdi/AtpResource.java b/examples/integrations/oci/atp-cdi/src/main/java/io/helidon/examples/integrations/oci/atp/cdi/AtpResource.java index 97ca809d83b..ef9b21cbc51 100644 --- a/examples/integrations/oci/atp-cdi/src/main/java/io/helidon/examples/integrations/oci/atp/cdi/AtpResource.java +++ b/examples/integrations/oci/atp-cdi/src/main/java/io/helidon/examples/integrations/oci/atp/cdi/AtpResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,27 +16,37 @@ package io.helidon.examples.integrations.oci.atp.cdi; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.Objects; -import java.util.Optional; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import com.oracle.bmc.database.Database; +import com.oracle.bmc.database.model.GenerateAutonomousDatabaseWalletDetails; +import com.oracle.bmc.database.requests.GenerateAutonomousDatabaseWalletRequest; +import com.oracle.bmc.database.responses.GenerateAutonomousDatabaseWalletResponse; +import com.oracle.bmc.http.internal.ResponseHelper; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.atp.OciAutonomousDb; -import io.helidon.integrations.oci.atp.GenerateAutonomousDatabaseWallet; - +import oracle.security.pki.OraclePKIProvider; import oracle.ucp.jdbc.PoolDataSource; - import org.eclipse.microprofile.config.inject.ConfigProperty; /** * JAX-RS resource - REST API for the atp example. @@ -45,16 +55,23 @@ public class AtpResource { private static final Logger LOGGER = Logger.getLogger(AtpResource.class.getName()); - private final OciAutonomousDb autonomousDb; + private final Database databaseClient; private final PoolDataSource atpDataSource; - private final String atpServiceName; + private final String atpTnsNetServiceName; + + private final String atpOcid; + private final String walletPassword; @Inject - AtpResource(OciAutonomousDb autonomousDb, @Named("atp") PoolDataSource atpDataSource, - @ConfigProperty(name = "oracle.ucp.jdbc.PoolDataSource.atp.serviceName") String atpServiceName) { - this.autonomousDb = autonomousDb; + AtpResource(Database databaseClient, @Named("atp") PoolDataSource atpDataSource, + @ConfigProperty(name = "oracle.ucp.jdbc.PoolDataSource.atp.tnsNetServiceName") String atpTnsNetServiceName, + @ConfigProperty(name = "oci.atp.ocid") String atpOcid, + @ConfigProperty(name = "oci.atp.walletPassword") String walletPassword) { + this.databaseClient = databaseClient; this.atpDataSource = Objects.requireNonNull(atpDataSource); - this.atpServiceName = atpServiceName; + this.atpTnsNetServiceName = atpTnsNetServiceName; + this.atpOcid = atpOcid; + this.walletPassword = walletPassword; } /** @@ -65,21 +82,34 @@ public class AtpResource { @GET @Path("/wallet") public Response generateWallet() { - ApiOptionalResponse ociResponse = autonomousDb.generateWallet(GenerateAutonomousDatabaseWallet.Request.builder()); - Optional entity = ociResponse.entity(); + ResponseHelper.shouldAutoCloseResponseInputStream(false); + GenerateAutonomousDatabaseWalletResponse walletResponse = + databaseClient.generateAutonomousDatabaseWallet( + GenerateAutonomousDatabaseWalletRequest.builder() + .autonomousDatabaseId(this.atpOcid) + .generateAutonomousDatabaseWalletDetails( + GenerateAutonomousDatabaseWalletDetails.builder() + .password(this.walletPassword) + .build()) + .build()); - if (entity.isEmpty()) { - LOGGER.log(Level.SEVERE, "GenerateAutonomousDatabaseWallet.Response is empty"); + if (walletResponse.getContentLength() == 0) { + LOGGER.log(Level.SEVERE, "GenerateAutonomousDatabaseWalletResponse is empty"); return Response.status(Response.Status.NOT_FOUND).build(); } - GenerateAutonomousDatabaseWallet.Response response = entity.get(); - GenerateAutonomousDatabaseWallet.WalletArchive walletArchive = response.walletArchive(); + byte[] walletContent = null; + try { + walletContent = walletResponse.getInputStream().readAllBytes(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error processing GenerateAutonomousDatabaseWalletResponse", e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } String returnEntity = null; try { - this.atpDataSource.setSSLContext(walletArchive.getSSLContext()); - this.atpDataSource.setURL(walletArchive.getJdbcUrl(this.atpServiceName)); - try( + this.atpDataSource.setSSLContext(getSSLContext(walletContent)); + this.atpDataSource.setURL(getJdbcUrl(walletContent, this.atpTnsNetServiceName)); + try ( Connection connection = this.atpDataSource.getConnection(); PreparedStatement ps = connection.prepareStatement("SELECT 'Hello world!!' FROM DUAL"); ResultSet rs = ps.executeQuery() @@ -94,5 +124,61 @@ public Response generateWallet() { return Response.status(Response.Status.OK).entity(returnEntity).build(); } + + /** + * Returns SSLContext based on cwallet.sso in wallet. + * + * @param walletContent + * @return SSLContext + */ + private static SSLContext getSSLContext(byte[] walletContent) throws IllegalStateException { + SSLContext sslContext = null; + try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(walletContent)))) { + ZipEntry entry = null; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals("cwallet.sso")) { + KeyStore keyStore = KeyStore.getInstance("SSO", new OraclePKIProvider()); + keyStore.load(zis, null); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); + trustManagerFactory.init(keyStore); + keyManagerFactory.init(keyStore, null); + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + } + zis.closeEntry(); + } + } catch (RuntimeException | Error throwMe) { + throw throwMe; + } catch (Exception e) { + throw new IllegalStateException("Error while getting SSLContext from wallet.", e); + } + return sslContext; + } + + /** + * Returns JDBC URL with connection description for the given service based on tnsnames.ora in wallet. + * + * @param walletContent + * @param tnsNetServiceName + * @return String + */ + private static String getJdbcUrl(byte[] walletContent, String tnsNetServiceName) throws IllegalStateException { + String jdbcUrl = null; + try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(walletContent)))) { + ZipEntry entry = null; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals("tnsnames.ora")) { + jdbcUrl = new String(zis.readAllBytes(), StandardCharsets.UTF_8) + .replaceFirst(tnsNetServiceName + "\\s*=\\s*", "jdbc:oracle:thin:@") + .replaceAll("\\n[^\\n]+", ""); + } + zis.closeEntry(); + } + } catch (IOException e) { + throw new IllegalStateException("Error while getting JDBC URL from wallet.", e); + } + return jdbcUrl; + } } diff --git a/examples/integrations/oci/atp-cdi/src/main/java/module-info.java b/examples/integrations/oci/atp-cdi/src/main/java/module-info.java index fd883711794..fbcc1c28d01 100644 --- a/examples/integrations/oci/atp-cdi/src/main/java/module-info.java +++ b/examples/integrations/oci/atp-cdi/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -21,17 +21,18 @@ requires java.logging; requires java.naming; requires java.sql; + requires jakarta.ws.rs; requires jakarta.inject; + requires microprofile.config.api; requires io.helidon.config.yaml.mp; - requires io.helidon.integrations.common.rest; - requires io.helidon.integrations.oci.cdi; - requires io.helidon.integrations.oci.atp; requires io.helidon.microprofile.cdi; + requires oci.java.sdk.shaded.full; requires com.oracle.database.ucp; + requires oraclepki; exports io.helidon.examples.integrations.oci.atp.cdi; } \ No newline at end of file diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/org.apache.httpcomponents/httpclient/native-image.properties b/examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties similarity index 89% rename from examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/org.apache.httpcomponents/httpclient/native-image.properties rename to examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties index 634a4eebe53..2ef2caf1d93 100644 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/META-INF/native-image/org.apache.httpcomponents/httpclient/native-image.properties +++ b/examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties @@ -13,5 +13,5 @@ # See the License for the specific language governing permissions and # limitations under the License. # -Args=\ ---initialize-at-run-time=org.apache.http.impl.auth.NTLMEngineImpl + +pattern=oracle.sql.converter.* diff --git a/examples/integrations/oci/atp-cdi/src/main/resources/application.yaml b/examples/integrations/oci/atp-cdi/src/main/resources/application.yaml index cc6be209e4b..de8655b3d5b 100644 --- a/examples/integrations/oci/atp-cdi/src/main/resources/application.yaml +++ b/examples/integrations/oci/atp-cdi/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -27,7 +27,7 @@ oracle: PoolDataSource: atp: connectionFactoryClassName: oracle.jdbc.pool.OracleDataSource - serviceName: "${atp.db.serviceName}" + tnsNetServiceName: "${atp.db.tnsNetServiceName}" user: "${atp.db.user}" password: "${atp.db.password}" diff --git a/examples/integrations/oci/atp-reactive/README.md b/examples/integrations/oci/atp-reactive/README.md index daa56519136..20b402558c1 100644 --- a/examples/integrations/oci/atp-reactive/README.md +++ b/examples/integrations/oci/atp-reactive/README.md @@ -8,7 +8,7 @@ Before running the test, make sure to update required properties in `application - oci.atp.ocid: This is OCID of your running ATP instance. - oci.atp.walletPassword: password to encrypt the keys inside the wallet. The password must be at least 8 characters long and must include at least 1 letter and either 1 numeric character or 1 special character. -- db.serviceName: serviceName of your database running inside OCI ATP. +- db.tnsNetServiceName: netServiceName of your database running inside OCI ATP as can be found in `tnsnames.ora` file. - db.userName: User to access your database running inside OCI ATP. - db.password: Password of user to access your database running inside OCI ATP. diff --git a/examples/integrations/oci/atp-reactive/pom.xml b/examples/integrations/oci/atp-reactive/pom.xml index e57e2a4c34e..86c09b99fa4 100644 --- a/examples/integrations/oci/atp-reactive/pom.xml +++ b/examples/integrations/oci/atp-reactive/pom.xml @@ -37,6 +37,21 @@ io.helidon.examples.integrations.oci.atp.reactive.OciAtpMain + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.webserver @@ -60,8 +75,18 @@ helidon-config-yaml - io.helidon.integrations.oci - helidon-integrations-oci-atp + com.oracle.oci.sdk + oci-java-sdk-shaded-full + + + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime diff --git a/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/AtpService.java b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/AtpService.java index 0e677e693ab..e8d2858d506 100644 --- a/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/AtpService.java +++ b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/AtpService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,34 +16,48 @@ package io.helidon.examples.integrations.oci.atp.reactive; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; import java.sql.SQLException; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import io.helidon.common.http.Http; import io.helidon.common.reactive.Single; import io.helidon.config.Config; import io.helidon.dbclient.DbClient; import io.helidon.dbclient.jdbc.JdbcDbClientProvider; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.atp.GenerateAutonomousDatabaseWallet; -import io.helidon.integrations.oci.atp.OciAutonomousDbRx; import io.helidon.webserver.Routing; import io.helidon.webserver.ServerRequest; import io.helidon.webserver.ServerResponse; import io.helidon.webserver.Service; +import com.oracle.bmc.database.DatabaseAsync; +import com.oracle.bmc.database.model.GenerateAutonomousDatabaseWalletDetails; +import com.oracle.bmc.database.requests.GenerateAutonomousDatabaseWalletRequest; +import com.oracle.bmc.database.responses.GenerateAutonomousDatabaseWalletResponse; import oracle.jdbc.pool.OracleDataSource; +import oracle.security.pki.OraclePKIProvider; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; class AtpService implements Service { private static final Logger LOGGER = Logger.getLogger(AtpService.class.getName()); - private final OciAutonomousDbRx autonomousDbRx; + private final DatabaseAsync databaseAsyncClient; private final Config config; - AtpService(OciAutonomousDbRx autonomousDbRx, Config config) { - this.autonomousDbRx = autonomousDbRx; + AtpService(DatabaseAsync databaseAsyncClient, Config config) { + this.databaseAsyncClient = databaseAsyncClient; this.config = config; } @@ -56,11 +70,42 @@ public void update(Routing.Rules rules) { * Generate wallet file for the configured ATP. */ private void generateWallet(ServerRequest req, ServerResponse res) { - autonomousDbRx.generateWallet(GenerateAutonomousDatabaseWallet.Request.builder()) - .flatMapOptional(ApiOptionalResponse::entity) - .map(GenerateAutonomousDatabaseWallet.Response::walletArchive) - .ifEmpty(() -> LOGGER.severe("Unable to obtain wallet!")) - .flatMapSingle(this::createDbClient) + OciResponseHandler walletHandler = + new OciResponseHandler<>(); + GenerateAutonomousDatabaseWalletResponse walletResponse = null; + try { + databaseAsyncClient.generateAutonomousDatabaseWallet( + GenerateAutonomousDatabaseWalletRequest.builder() + .autonomousDatabaseId(config.get("oci.atp.ocid").asString().get()) + .generateAutonomousDatabaseWalletDetails( + GenerateAutonomousDatabaseWalletDetails.builder() + .password(config.get("oci.atp.walletPassword").asString().get()) + .build()) + .build(), walletHandler); + walletResponse = walletHandler.waitForCompletion(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error waiting for GenerateAutonomousDatabaseWalletResponse", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } + + if (walletResponse.getContentLength() == 0) { + LOGGER.log(Level.SEVERE, "GenerateAutonomousDatabaseWalletResponse is empty"); + res.status(Http.Status.NOT_FOUND_404).send(); + return; + } + + byte[] walletContent = null; + try { + walletContent = walletResponse.getInputStream().readAllBytes(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error processing GenerateAutonomousDatabaseWalletResponse", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } + + createDbClient(walletContent) .flatMap(dbClient -> dbClient.execute(exec -> exec.query("SELECT 'Hello world!!' FROM DUAL"))) .first() .map(dbRow -> dbRow.column(1).as(String.class)) @@ -69,30 +114,85 @@ private void generateWallet(ServerRequest req, ServerResponse res) { .forSingle(res::send); } - Single createDbClient(GenerateAutonomousDatabaseWallet.WalletArchive walletArchive) { + Single createDbClient(byte[] walletContent) { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); try { - pds.setSSLContext(walletArchive.getSSLContext()); - pds.setURL(walletArchive.getJdbcUrl(config.get("db.serviceName") + pds.setSSLContext(getSSLContext(walletContent)); + pds.setURL(getJdbcUrl(walletContent, config.get("db.tnsNetServiceName") .as(String.class) - .orElseThrow(() -> new IllegalStateException("Missing serviceName!!")))); + .orElseThrow(() -> new IllegalStateException("Missing tnsNetServiceName!!")))); pds.setUser(config.get("db.userName").as(String.class).orElse("ADMIN")); pds.setPassword(config.get("db.password") - .as(String.class) - .orElseThrow(() -> new IllegalStateException("Missing password!!"))); + .as(String.class) + .orElseThrow(() -> new IllegalStateException("Missing password!!"))); pds.setConnectionFactoryClassName(OracleDataSource.class.getName()); } catch (SQLException e) { LOGGER.log(Level.SEVERE, "Error setting up PoolDataSource", e); return Single.error(e); } return Single.just(new JdbcDbClientProvider().builder() - .connectionPool(() -> { - try { - return pds.getConnection(); - } catch (SQLException e) { - throw new IllegalStateException("Error while setting up new connection", e); - } - }) - .build()); + .connectionPool(() -> { + try { + return pds.getConnection(); + } catch (SQLException e) { + throw new IllegalStateException("Error while setting up new connection", e); + } + }) + .build()); + } + + /** + * Returns SSLContext based on cwallet.sso in wallet. + * + * @return SSLContext + */ + private static SSLContext getSSLContext(byte[] walletContent) throws IllegalStateException { + SSLContext sslContext = null; + try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(walletContent)))) { + ZipEntry entry = null; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals("cwallet.sso")) { + KeyStore keyStore = KeyStore.getInstance("SSO", new OraclePKIProvider()); + keyStore.load(zis, null); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); + trustManagerFactory.init(keyStore); + keyManagerFactory.init(keyStore, null); + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + } + zis.closeEntry(); + } + } catch (RuntimeException | Error throwMe) { + throw throwMe; + } catch (Exception e) { + throw new IllegalStateException("Error while getting SSLContext from wallet.", e); + } + return sslContext; + } + + /** + * Returns JDBC URL with connection description for the given service based on tnsnames.ora in wallet. + * + * @param walletContent + * @param tnsNetServiceName + * @return String + */ + private static String getJdbcUrl(byte[] walletContent, String tnsNetServiceName) throws IllegalStateException { + String jdbcUrl = null; + try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(walletContent)))) { + ZipEntry entry = null; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals("tnsnames.ora")) { + jdbcUrl = new String(zis.readAllBytes(), StandardCharsets.UTF_8) + .replaceFirst(tnsNetServiceName + "\\s*=\\s*", "jdbc:oracle:thin:@") + .replaceAll("\\n[^\\n]+", ""); + } + zis.closeEntry(); + } + } catch (IOException e) { + throw new IllegalStateException("Error while getting JDBC URL from wallet.", e); + } + return jdbcUrl; } } diff --git a/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciAtpMain.java b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciAtpMain.java index c86f3a35bee..b7768c4493c 100644 --- a/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciAtpMain.java +++ b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciAtpMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,14 +16,19 @@ package io.helidon.examples.integrations.oci.atp.reactive; +import java.io.IOException; + import io.helidon.common.LogConfig; import io.helidon.config.Config; -import io.helidon.integrations.oci.atp.OciAutonomousDbRx; import io.helidon.webserver.Routing; import io.helidon.webserver.WebServer; -import static io.helidon.config.ConfigSources.classpath; -import static io.helidon.config.ConfigSources.file; +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.AuthenticationDetailsProvider; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.database.DatabaseAsync; +import com.oracle.bmc.database.DatabaseAsyncClient; +import com.oracle.bmc.model.BmcException; /** * Main class of the example. @@ -41,24 +46,26 @@ private OciAtpMain() { * * @param args command line arguments. */ - public static void main(String[] args) { + public static void main(String[] args) throws IOException { // load logging configuration LogConfig.configureRuntime(); // By default this will pick up application.yaml from the classpath Config config = Config.create(); - Config ociConfig = config.get("oci"); - // this requires OCI configuration in the usual place // ~/.oci/config - OciAutonomousDbRx autonomousDbRx = OciAutonomousDbRx.create(ociConfig); + AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(ConfigFileReader.parseDefault()); + DatabaseAsync databaseAsyncClient = DatabaseAsyncClient.builder().build(authProvider); // Prepare routing for the server WebServer server = WebServer.builder() .config(config.get("server")) .routing(Routing.builder() - .register("/atp", new AtpService(autonomousDbRx, config))) + .register("/atp", new AtpService(databaseAsyncClient, config)) + // OCI SDK error handling + .error(BmcException.class, (req, res, ex) -> res.status(ex.getStatusCode()) + .send(ex.getMessage()))) .build(); // Start the server and print some info. diff --git a/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciResponseHandler.java b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciResponseHandler.java new file mode 100644 index 00000000000..0eac2a44fa5 --- /dev/null +++ b/examples/integrations/oci/atp-reactive/src/main/java/io/helidon/examples/integrations/oci/atp/reactive/OciResponseHandler.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.helidon.examples.integrations.oci.atp.reactive; + +import java.util.concurrent.CountDownLatch; + +import com.oracle.bmc.responses.AsyncHandler; + +final class OciResponseHandler implements AsyncHandler { + private OUT item; + private Throwable failed = null; + private CountDownLatch latch = new CountDownLatch(1); + + protected OUT waitForCompletion() throws Exception { + latch.await(); + if (failed != null) { + if (failed instanceof Exception) { + throw (Exception) failed; + } + throw (Error) failed; + } + return item; + } + + @Override + public void onSuccess(IN request, OUT response) { + item = response; + latch.countDown(); + } + + @Override + public void onError(IN request, Throwable error) { + failed = error; + latch.countDown(); + } +} diff --git a/examples/integrations/oci/atp-reactive/src/main/java/module-info.java b/examples/integrations/oci/atp-reactive/src/main/java/module-info.java index e5a19a920d0..324d77d43ce 100644 --- a/examples/integrations/oci/atp-reactive/src/main/java/module-info.java +++ b/examples/integrations/oci/atp-reactive/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -19,18 +19,19 @@ */ module io.helidon.examples.integrations.oci.atp.reactive { requires java.logging; - requires jakarta.json; requires java.sql; requires io.helidon.common.http; requires io.helidon.common.reactive; requires io.helidon.dbclient; requires io.helidon.dbclient.jdbc; - requires io.helidon.integrations.oci.atp; requires io.helidon.webserver; + requires oci.java.sdk.shaded.full; + requires com.oracle.database.jdbc; requires com.oracle.database.ucp; + requires oraclepki; exports io.helidon.examples.integrations.oci.atp.reactive; } \ No newline at end of file diff --git a/examples/integrations/cdi/oci-objectstorage/src/main/resources/commons-logging.properties b/examples/integrations/oci/atp-reactive/src/main/resources/META-INF/helidon/serial-config.properties similarity index 88% rename from examples/integrations/cdi/oci-objectstorage/src/main/resources/commons-logging.properties rename to examples/integrations/oci/atp-reactive/src/main/resources/META-INF/helidon/serial-config.properties index 4c75a4a82fc..2ef2caf1d93 100644 --- a/examples/integrations/cdi/oci-objectstorage/src/main/resources/commons-logging.properties +++ b/examples/integrations/oci/atp-reactive/src/main/resources/META-INF/helidon/serial-config.properties @@ -13,4 +13,5 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger + +pattern=oracle.sql.converter.* diff --git a/examples/integrations/oci/atp-reactive/src/main/resources/application.yaml b/examples/integrations/oci/atp-reactive/src/main/resources/application.yaml index a563de563cc..156f26ed159 100644 --- a/examples/integrations/oci/atp-reactive/src/main/resources/application.yaml +++ b/examples/integrations/oci/atp-reactive/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -24,7 +24,7 @@ server: db: userName: "${atp.db.userName}" password: "${atp.db.password}" - serviceName: "${atp.db.serviceName}" + tnsNetServiceName: "${atp.db.tnsNetServiceName}" oci: atp: diff --git a/examples/integrations/oci/metrics-reactive/pom.xml b/examples/integrations/oci/metrics-reactive/pom.xml index 9fcf823b62f..6aec9ce12f2 100644 --- a/examples/integrations/oci/metrics-reactive/pom.xml +++ b/examples/integrations/oci/metrics-reactive/pom.xml @@ -37,19 +37,44 @@ Helidon Examples Integration OCI Metrics Reactive Reactive integration with OCI Metrics. + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.webserver helidon-webserver - - io.helidon.integrations.oci - helidon-integrations-oci-telemetry - io.helidon.config helidon-config-yaml + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + + + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime + diff --git a/examples/integrations/oci/metrics-reactive/src/main/java/io/helidon/examples/integrations/oci/telemetry/reactive/OciMetricsMain.java b/examples/integrations/oci/metrics-reactive/src/main/java/io/helidon/examples/integrations/oci/telemetry/reactive/OciMetricsMain.java index bae9fc6d089..400edcd731a 100644 --- a/examples/integrations/oci/metrics-reactive/src/main/java/io/helidon/examples/integrations/oci/telemetry/reactive/OciMetricsMain.java +++ b/examples/integrations/oci/metrics-reactive/src/main/java/io/helidon/examples/integrations/oci/telemetry/reactive/OciMetricsMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,12 +18,28 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; import io.helidon.common.LogConfig; import io.helidon.config.Config; -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.oci.telemetry.OciMetricsRx; -import io.helidon.integrations.oci.telemetry.PostMetricData; + +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.AuthenticationDetailsProvider; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.monitoring.MonitoringAsync; +import com.oracle.bmc.monitoring.MonitoringAsyncClient; +import com.oracle.bmc.monitoring.model.Datapoint; +import com.oracle.bmc.monitoring.model.FailedMetricRecord; +import com.oracle.bmc.monitoring.model.MetricDataDetails; +import com.oracle.bmc.monitoring.model.PostMetricDataDetails; +import com.oracle.bmc.monitoring.model.PostMetricDataResponseDetails; +import com.oracle.bmc.monitoring.requests.PostMetricDataRequest; +import com.oracle.bmc.monitoring.responses.PostMetricDataResponse; +import com.oracle.bmc.responses.AsyncHandler; import static io.helidon.config.ConfigSources.classpath; import static io.helidon.config.ConfigSources.file; @@ -32,6 +48,7 @@ * OCI Metrics example. */ public final class OciMetricsMain { + private OciMetricsMain() { } @@ -39,7 +56,7 @@ private OciMetricsMain() { * Main method. * @param args ignored */ - public static void main(String[] args) { + public static void main(String[] args) throws Exception { LogConfig.configureRuntime(); // as I cannot share my configuration of OCI, let's combine the configuration // from my home directory with the one compiled into the jar @@ -47,50 +64,66 @@ public static void main(String[] args) { // or use the same approach Config config = buildConfig(); - OciMetricsRx metrics = OciMetricsRx.create(config.get("oci")); - - String compartmentId = config.get("oci.metrics.compartment-ocid").asString().get(); - - Instant now = Instant.now(); - PostMetricData.Request request = PostMetricData.Request.builder() - .addMetricData(PostMetricData.MetricData.builder() - .compartmentId(compartmentId) - // Add a few data points to see something in the console - .addDataPoint(now.minus(10, ChronoUnit.SECONDS), 101) - .addDataPoint(now.minus(5, ChronoUnit.SECONDS), 123) - .addDataPoint(now, 149) - .addDimension("resourceId", "myresourceid") - .addMetaData("unit", "cm") - .name("my_app.jump") - .namespace("helidon_examples")); + // this requires OCI configuration in the usual place + // ~/.oci/config + AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(ConfigFileReader.parseDefault()); + MonitoringAsync monitoringAsyncClient = new MonitoringAsyncClient(authProvider); + monitoringAsyncClient.setEndpoint(monitoringAsyncClient.getEndpoint().replace("telemetry.", "telemetry-ingestion.")); + PostMetricDataRequest postMetricDataRequest = PostMetricDataRequest.builder() + .postMetricDataDetails(getPostMetricDataDetails(config)) + .build(); /* * Invoke the API call. I use .await() to block the call, as otherwise our * main method would finish without waiting for the response. * In a real reactive application, this should not be done (as you would write the response * to a server response or use other reactive/non-blocking APIs). */ - metrics.postMetricData(request) - .peek(it -> { - System.out.println("PostMetrics: " + it.status()); - printHeaders(it); - }) - .forSingle(it -> { - int count = it.failedMetricsCount(); - System.out.println("Failed count: " + count); - if (count > 0) { - System.out.println("Failed metrics:"); - for (PostMetricData.FailedMetric failedMetric : it.failedMetrics()) { - System.out.println("\t" + failedMetric.message() + ": " + failedMetric.failedDataJson()); - } - } - }) - .await(); + ResponseHandler monitoringHandler = + new ResponseHandler<>(); + monitoringAsyncClient.postMetricData(postMetricDataRequest, monitoringHandler); + PostMetricDataResponse postMetricDataResponse = monitoringHandler.waitForCompletion(); + PostMetricDataResponseDetails postMetricDataResponseDetails = postMetricDataResponse.getPostMetricDataResponseDetails(); + int count = postMetricDataResponseDetails.getFailedMetricsCount(); + System.out.println("Failed count: " + count); + if (count > 0) { + System.out.println("Failed metrics:"); + for (FailedMetricRecord failedMetric : postMetricDataResponseDetails.getFailedMetrics()) { + System.out.println("\t" + failedMetric.getMessage() + ": " + failedMetric.getMetricData()); + } + } } - private static void printHeaders(ApiEntityResponse response) { - System.out.println("\tHeaders:"); - response.headers().toMap().forEach((key, values) -> System.out.println("\t\t" + key + ": " + values)); + private static PostMetricDataDetails getPostMetricDataDetails(Config config) { + String compartmentId = config.get("oci.metrics.compartment-ocid").asString().get(); + Instant now = Instant.now(); + return PostMetricDataDetails.builder() + .metricData( + Arrays.asList( + MetricDataDetails.builder() + .compartmentId(compartmentId) + // Add a few data points to see something in the console + .datapoints( + Arrays.asList( + Datapoint.builder() + .timestamp(Date.from( + now.minus(10, ChronoUnit.SECONDS) + )) + .value(101.00) + .build(), + Datapoint.builder() + .timestamp(Date.from(now)) + .value(149.00) + .build() + )) + .dimensions( + makeMap("resourceId", "myresourceid", + "unit", "cm")) + .name("my_app.jump") + .namespace("helidon_examples") + .build() + )) + .batchAtomicity(PostMetricDataDetails.BatchAtomicity.NonAtomic).build(); } private static Config buildConfig() { @@ -102,4 +135,41 @@ private static Config buildConfig() { classpath("application.yaml")) .build(); } + + private static Map makeMap(String... data) { + Map map = new HashMap<>(); + for (int i = 0; i < data.length; i += 2) { + map.put(data[i], data[i + 1]); + } + return map; + } + + private static class ResponseHandler implements AsyncHandler { + private OUT item; + private Throwable failed = null; + private CountDownLatch latch = new CountDownLatch(1); + + private OUT waitForCompletion() throws Exception { + latch.await(); + if (failed != null) { + if (failed instanceof Exception) { + throw (Exception) failed; + } + throw (Error) failed; + } + return item; + } + + @Override + public void onSuccess(IN request, OUT response) { + item = response; + latch.countDown(); + } + + @Override + public void onError(IN request, Throwable error) { + failed = error; + latch.countDown(); + } + } } diff --git a/examples/integrations/oci/metrics-reactive/src/main/java/module-info.java b/examples/integrations/oci/metrics-reactive/src/main/java/module-info.java index 5ea0f5db761..6b8fef43bcb 100644 --- a/examples/integrations/oci/metrics-reactive/src/main/java/module-info.java +++ b/examples/integrations/oci/metrics-reactive/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -21,7 +21,8 @@ requires java.logging; requires io.helidon.config; - requires io.helidon.integrations.oci.telemetry; + + requires oci.java.sdk.shaded.full; exports io.helidon.examples.integrations.oci.telemetry.reactive; } \ No newline at end of file diff --git a/examples/integrations/oci/objectstorage-cdi/pom.xml b/examples/integrations/oci/objectstorage-cdi/pom.xml index 4a808386fb5..5a82d74cc9d 100644 --- a/examples/integrations/oci/objectstorage-cdi/pom.xml +++ b/examples/integrations/oci/objectstorage-cdi/pom.xml @@ -37,22 +37,44 @@ io.helidon.examples.integrations.oci.objectstorage.cdi.ObjectStorageCdiMain + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.microprofile.bundles helidon-microprofile - io.helidon.integrations.oci - helidon-integrations-oci-cdi + io.helidon.integrations.oci.sdk + helidon-integrations-oci-sdk-cdi + runtime + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage + org.slf4j + slf4j-jdk14 + runtime - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage-health + org.bouncycastle + bcpkix-jdk15on + runtime io.helidon.config diff --git a/examples/integrations/oci/objectstorage-cdi/src/main/java/io/helidon/examples/integrations/oci/objectstorage/cdi/ObjectStorageResource.java b/examples/integrations/oci/objectstorage-cdi/src/main/java/io/helidon/examples/integrations/oci/objectstorage/cdi/ObjectStorageResource.java index 4eb47cf70f8..905e3e6cdef 100644 --- a/examples/integrations/oci/objectstorage-cdi/src/main/java/io/helidon/examples/integrations/oci/objectstorage/cdi/ObjectStorageResource.java +++ b/examples/integrations/oci/objectstorage-cdi/src/main/java/io/helidon/examples/integrations/oci/objectstorage/cdi/ObjectStorageResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,28 +16,32 @@ package io.helidon.examples.integrations.oci.objectstorage.cdi; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; -import java.nio.channels.Channels; -import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; import io.helidon.common.http.Http; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.objectstorage.DeleteObject; -import io.helidon.integrations.oci.objectstorage.GetObject; -import io.helidon.integrations.oci.objectstorage.OciObjectStorage; -import io.helidon.integrations.oci.objectstorage.PutObject; +import com.oracle.bmc.objectstorage.ObjectStorage; +import com.oracle.bmc.objectstorage.requests.DeleteObjectRequest; +import com.oracle.bmc.objectstorage.requests.GetNamespaceRequest; +import com.oracle.bmc.objectstorage.requests.GetObjectRequest; +import com.oracle.bmc.objectstorage.requests.PutObjectRequest; +import com.oracle.bmc.objectstorage.responses.DeleteObjectResponse; +import com.oracle.bmc.objectstorage.responses.GetNamespaceResponse; +import com.oracle.bmc.objectstorage.responses.GetObjectResponse; +import com.oracle.bmc.objectstorage.responses.PutObjectResponse; import jakarta.inject.Inject; import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; -import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.StreamingOutput; import org.eclipse.microprofile.config.inject.ConfigProperty; /** @@ -45,15 +49,20 @@ */ @Path("/files") public class ObjectStorageResource { - private final OciObjectStorage objectStorage; + private static final Logger LOGGER = Logger.getLogger(ObjectStorageResource.class.getName()); + private final ObjectStorage objectStorageClient; + private final String namespaceName; private final String bucketName; @Inject - ObjectStorageResource(OciObjectStorage objectStorage, - @ConfigProperty(name = "oci.properties.objectstorage-bucket") - String bucketName) { - this.objectStorage = objectStorage; + ObjectStorageResource(ObjectStorage objectStorageClient, + @ConfigProperty(name = "oci.objectstorage.bucketName") + String bucketName) { + this.objectStorageClient = objectStorageClient; this.bucketName = bucketName; + GetNamespaceResponse namespaceResponse = + this.objectStorageClient.getNamespace(GetNamespaceRequest.builder().build()); + this.namespaceName = namespaceResponse.getValue(); } /** @@ -65,62 +74,66 @@ public class ObjectStorageResource { @GET @Path("/file/{file-name}") public Response download(@PathParam("file-name") String fileName) { - ApiOptionalResponse ociResponse = objectStorage.getObject(GetObject.Request.builder() - .bucket(bucketName) - .objectName(fileName)); - Optional entity = ociResponse.entity(); - - if (entity.isEmpty()) { + GetObjectResponse getObjectResponse = + objectStorageClient.getObject( + GetObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(fileName) + .build()); + + if (getObjectResponse.getContentLength() == 0) { + LOGGER.log(Level.SEVERE, "GetObjectResponse is empty"); return Response.status(Response.Status.NOT_FOUND).build(); } - GetObject.Response response = entity.get(); - - StreamingOutput stream = output -> response.writeTo(Channels.newChannel(output)); - - Response.ResponseBuilder ok = Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM_TYPE) - .header(Http.Header.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") - .header("opc-request-id", ociResponse.headers().first("opc-request-id").orElse("")) - .header("request-id", ociResponse.requestId()); - - ociResponse.headers() - .first(Http.Header.CONTENT_TYPE) - .ifPresent(ok::type); - - ociResponse.headers() - .first(Http.Header.CONTENT_LENGTH) - .ifPresent(it -> ok.header(Http.Header.CONTENT_LENGTH, it)); - - return ok.build(); + try (InputStream fileStream = getObjectResponse.getInputStream()) { + byte[] objectContent = fileStream.readAllBytes(); + Response.ResponseBuilder ok = Response.ok(objectContent) + .header(Http.Header.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") + .header("opc-request-id", getObjectResponse.getOpcRequestId()) + .header("request-id", getObjectResponse.getOpcClientRequestId()) + .header(Http.Header.CONTENT_LENGTH, getObjectResponse.getContentLength()); + + return ok.build(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error processing GetObjectResponse", e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } } /** * Upload a file to object storage. * * @param fileName name of the object - * @param contentLength content length (required) - * @param type content type - * @param entity the entity used for upload * @return response */ @POST @Path("/file/{fileName}") - public Response upload(@PathParam("fileName") String fileName, - @HeaderParam("Content-Length") long contentLength, - @HeaderParam("Content-Type") @DefaultValue("application/octet-stream") String type, - InputStream entity) { - PutObject.Response response = objectStorage.putObject(PutObject.Request.builder() - .contentLength(contentLength) - .bucket(bucketName) - .requestMediaType(io.helidon.common.http.MediaType - .parse(type)) - .objectName(fileName), - Channels.newChannel(entity)); - - return Response.status(response.status().code()) - .header("opc-request-id", response.headers().first("opc-request-id").orElse("")) - .header("request-id", response.requestId()) - .build(); + public Response upload(@PathParam("fileName") String fileName) { + + PutObjectRequest putObjectRequest = null; + try (InputStream stream = new FileInputStream(System.getProperty("user.dir") + File.separator + fileName)) { + byte[] contents = stream.readAllBytes(); + putObjectRequest = + PutObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(fileName) + .putObjectBody(new ByteArrayInputStream(contents)) + .contentLength(Long.valueOf(contents.length)) + .build(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error creating PutObjectRequest", e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + PutObjectResponse putObjectResponse = objectStorageClient.putObject(putObjectRequest); + + Response.ResponseBuilder ok = Response.ok() + .header("opc-request-id", putObjectResponse.getOpcRequestId()) + .header("request-id", putObjectResponse.getOpcClientRequestId()); + + return ok.build(); } /** @@ -132,13 +145,15 @@ public Response upload(@PathParam("fileName") String fileName, @DELETE @Path("/file/{file-name}") public Response delete(@PathParam("file-name") String fileName) { - DeleteObject.Response response = objectStorage.deleteObject(DeleteObject.Request.builder() - .bucket(bucketName) - .objectName(fileName)); - - return Response.status(response.status().code()) - .header("opc-request-id", response.headers().first("opc-request-id").orElse("")) - .header("request-id", response.requestId()) - .build(); + DeleteObjectResponse deleteObjectResponse = objectStorageClient.deleteObject(DeleteObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(fileName) + .build()); + Response.ResponseBuilder ok = Response.ok() + .header("opc-request-id", deleteObjectResponse.getOpcRequestId()) + .header("request-id", deleteObjectResponse.getOpcClientRequestId()); + + return ok.build(); } } diff --git a/examples/integrations/oci/objectstorage-cdi/src/main/java/module-info.java b/examples/integrations/oci/objectstorage-cdi/src/main/java/module-info.java index 802e0f5e07d..1ce5e41ea71 100644 --- a/examples/integrations/oci/objectstorage-cdi/src/main/java/module-info.java +++ b/examples/integrations/oci/objectstorage-cdi/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,18 +18,19 @@ * Example of integration with OCI object storage in a CDI application. */ module io.helidon.examples.integrations.oci.objectstorage.cdi { - requires jakarta.ws.rs; + requires java.logging; requires jakarta.json.bind; + + requires jakarta.ws.rs; requires jakarta.inject; + requires microprofile.config.api; requires io.helidon.config.yaml.mp; requires io.helidon.common.http; - requires io.helidon.integrations.common.rest; - requires io.helidon.integrations.oci.cdi; - requires io.helidon.integrations.oci.objectstorage; requires io.helidon.microprofile.cdi; - requires io.helidon.integrations.oci.objectstorage.health; + + requires oci.java.sdk.shaded.full; exports io.helidon.examples.integrations.oci.objectstorage.cdi; diff --git a/examples/integrations/oci/objectstorage-cdi/src/main/resources/application.yaml b/examples/integrations/oci/objectstorage-cdi/src/main/resources/application.yaml index be68552923d..f0b991ad95b 100644 --- a/examples/integrations/oci/objectstorage-cdi/src/main/resources/application.yaml +++ b/examples/integrations/oci/objectstorage-cdi/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -23,5 +23,4 @@ server: # oci: objectstorage: - namespace: "${oci.properties.objectstorage-namespace}" - healthchecks: [ "${oci.properties.objectstorage-bucket}" ] + bucketName: "${oci.properties.objectstorage-bucketName}" diff --git a/examples/integrations/oci/objectstorage-reactive/frank.png b/examples/integrations/oci/objectstorage-reactive/frank.png deleted file mode 100644 index 51a13d8db8bc02ad1613da3b809cff394462a77a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10516 zcma)ic{tR6^zWdEk`_{gN)nPip&3z#v6F2ukzFEt7-LNl%AO_rmfcu~8B_?_w-95= zzBA0288i3e`+M&5-22ac?jQ5a@_8?3&ikC#>%7l7A9S_VSXsDOKp+sS`m?9{AQ0U# z@Va=05%}eeM&KLZjoIy)sRsyj_R`6VZfH7?0s`FtsXtXR^qt(C_IEQMx5Dk>Fg|dT zdqJnGPCbrd`;#X$SuH&9@s>(4|NB&VN9)+dW)s=2_pd%)YwY@!px>#TI%&uoU*N^% zG_Uz(pghaQI-*RlV4)^vJtgOrOyb{O>pZ&5eA&PhCtmD;Jr?%dmWtzx#%N3W@$Hx|<_~p0Yg~BI&BO3yyQhPm zM$d5cc!lAN`{@E+j9B9D{EBhV%*VW%JdyS)9Z)tK2vYL96eee72|0rRfd<4Mr~t3WER#il_UZA^)GlPFDJVul86@nVpXEYWc|z2HB(K znR%W3`<%$2$nb@#pWIuM`@f4BGz_>LqDf+Gj zY3nN&aTaW37k|9I&KKwTJ}!vK^BL(c^|HK%UQiN5cj^kVC2!cej*e@52)^dXfhITh zvnli4AYMFKx~nm%m;mN2r^9{H=IQnW9pkq4zJ*8DTwMJGrG>-@vb58%ccUl7I8s z!Q%#!*~!FdWbOKUsmti(p~tyT;BkB2TSLxM*kfn@rmqT6<|F~Z)zh*vDcN_=APfuR zhI6_PU7i{4$Y#&)kG3DuJUc%R*!;6d=W*~{!F}3l#{ZGUQS0GZ>^<&nTC6ep=fWUG z1EsL+Rq}K_^7~hxz!?27PGxx9l^iJ~`MZnG@y+Suj{AUvY#RVWUxcXy%;p0MOOFTp zzga)4wz4JboTAk-PV(oj?V%^pER4aINhGXeEGMj;=E3n6|l zsy%^`prY?MAxFPskB>SvCTM#0Vj;1Z4t5H~90* zVa@(`3w+kRQo z2rhN_BiZyY>__!?JA>jUG5NkD%?oZbEFyv>cNVJz?0A2H+BK~t5oZ$tM)~g+;hQ_zi(V_|>syykF_4J#ARLBmo`N%m&UgKfX z#H|}&(ydodAB^OPU%F}RlHEMve10l2te4>pERCJ*IZdYXM|fv>xXWhk+uj55v_$HX z|Ily+niNryK8pXDKK_LEP7v@>rSgEVz+C!5xYMuW0=i)af7mFu7rT_4x1AnlUk)|v zelI*H=I8-xe0j7frNVcyByDo$H3TDgMUvJ$e-p{rQI^(lxAnK8c>;BQsvJx55i6gX zCp=q$vRbyYMUPjay^W(F(q9ITqZ%h`N`-5uW^>@e15Ojd8nw?XYMZpW9`j|<6aL=9 zc=iraET-_Ig{qXj>g`X`&9t!dU*)sn10rkii2UDMtG1#PeT(j>w*q&hCTsCNR-Do zL%vlbFbwj|ROp)c!%`iQEkrW9ta1LI;u!uTCqr)<6JzwpaOyH|nis zffi3m=QBPpzuxXDuiwJ|?I@;b$(2Gg!{uZf|Acoe=OJm&jp5Y4y#%X0-<1|iq@34d zwIf4jMAjD({yQj5l8sx`($G_H+TV@&x^auZ{v^FFHSu-wmHJL_>Lqk+@mSP zTV9OUV0@`EcF33Mb#Q>>8vf#HeolD>I<)-UpDu?m5f^eDQp|xn0?^Ds; zU+B&<^O{0TyB;~R-C@JTusT`n8)`4*<8@K9bunv{yI#8fI%}TwXo-#V-r;ZPc%Dwn z>{|EF94sZAa|}GAmMvE0$d=8P^{ow8@%^xwN9nCjK@aXuoG6Q^=@)UXy4ps>Tc4-J zi6CR6r|#7gZu0CI|we)r4r&r66j1|OR>$- zg2|&C6X(^h2^-eh!>e)@d};UBB@kChvX4y9A<<-J1c>__)kcE2oh2R8+X)B~3D2sm zy+3e+zmqg9{9Eph5LGQv`q75^5|M|X!dJGdo9}~wh+w(kn%Sj zU;HycPAqB>AhUYyV))0qXV3ehE%{f|$T}-o5#O<{giia2+hBp%^6q2L`B@*I(;DUvE{s zwoa^j0PS*FYGTm(sam^o?<(K+!}9A>6nV&^rc?KqNjJHtf8V1E>Pi+C{YSoT!H)Xv z4IBF5<6Bf~W2$b|a@+o%v08uAWyj%h^jxbTK3G`8{Ae>TFLk<^HCo`(pli!qk-4+I zT<~RNPNXnO$k9IjInd9qz)#~l$`?5UFRo#A+P3^*YqddC` z#l;$7+xoGM-36nkQyG1%ihU-5s{QwxOT~Iy@-}hDZv@1)L^<@C0D$U}4?|?K5H@iO zh0dzivf+td>cY@=-dNzmYdf8y2(cGI&8bc0xJ*i99hep*-+7oI z|8LiT$+O{IF!Je;)sb6kiPeg+=nnJooiU#SHk$iP?@S%uC&o5BheeQ!BUN;)m!UBI zO7~Yc*{pdqRi!{ zvbk_|q&EhDSzDJ$6nO!~#ttJWgc!bd4h!t1&m3T)&-q+Ju=N*Ah!rhI{!Qw7-kF$c zq3gt`RBTFLAVrt++5|K44;^c)?b>DbQdJo7sD=FRHwrDzWxTzkle}-gUOsQU(aooC z6`$EEbde-;qSP~%JDKOqoE>uAyG$43){W2qH8F~UCH3#JMdUpt?^%8_x6zZz=!r#b zDsOk_M`eF3@9Z5r=bJm#MA^M3#NKUe{y`iQjv9>YlDz^SPF}Jc8f|Qz!5=&+D5E6u zo*=Su7(ze%H6`}+(O+|5Q?~!v6!kuq>0o0z-=rG~K0|F;ZCrHdBX=W%f za?9C;Ri@XU%IU9A=En#TMcg2kQE$FQ3|}-OnOJ$>DRCZ`3=WLt!;0m|dQGg7NgHTw zrR;pRQ%$2t-68&jQ3h$n%SQ?eh~M;d`*JV)S`HHxA{qipAd{Os5nB@-g7j z-f4-6I%E?WbAcGgKJTdv4p=mQJGGb8<{I1NZd)Z4_fCMPv8BRK;nVbOVuTMw`cW2R z*$k7RFjwtplR!a9?pZVQ-Fj8xcp!i=;;lecCJx4*{*|S}{*!{T@vKVI@B9ieeiZ zT>TDzq$-a^OOhUBd-`2TvKbNUu!>wv*Cg1;7C**cGuMqb2MxaPUmu->56vqIFYOHv z5wgw4M*pP9Hj%aHaXzBQW=$cH{x{RQ|* z?sR~$)Tvk(*xFxqJ!vPA49i!(J}t=%aO)dh;OS+;421|thNL0(rWB`yw_cf6(0sl~duEkwB$@jkmtYM>Kr+%Gtxmv-deAaDMDl;F9-#(YdL(Rg<=uB5TR1^v!qNonMW@ z$xW5F_<~ri4zLF@?kd=svF|!s5OQ9^`;nz8pHX5J><3n{j91SJ_6l&2Q|o6Vn4E|^ z1+j=B1YvNF@igtzqv|6)|G$H^%$!^enwLfmhJQ&nWMmDxNe68)6k%*vZCkQVZgbDSYDIZJUHWI`%fLv3!C3u_ytNBB)Da6 zpkn4Aet>Ds?AofqfuE{|dWpD;>o2G+YCn69$uMPTJi+6L8F9i>1BR(@FY+m8wh}Z} zP=Gn0ugZ-nmMXJ#ywzandLx9RZaSS?P%~OiR;r0_e`ET;M6GLr*55*@i zo&oUiu}2t9DTMD71c;U!Lue8j^#6U73!%L~G0Rk;9a+IrOJE#|k)|s_6uiqS{p76T z{9TO|28YAhlVk?I)s@}oAH%Gi5)8B57d$=LEZ4^bW2$#e3f$A8zQw>USFarf0O_I% zMr8F>3f2{Myw|_2rzTl&JsC!6&C4S^YSt-SKg#pmX4uKQAvzy#`fIH>L*F^2pCc}> zP++sj=fw3WmnhiGo^2C-c+<#~84>4T77K3HVPM-fKws41&7lO7P#rOZu2)ghPTUTy z9*0m3Bg%LSjHuHO#~x{{M|JVGAP+4hie+wFq<>yQ1#qVRdaKR`Yz-jO#W1WQz4c4Z z-fn5wT`#wSw^NC5VmX?+?CnK?P^Y&V_B$hzyAA}1;7AS|)yhs4Ecj1{^*^-(cnp{B8Obux!WN1(M-A_D-4GV9Qr?g_y(d zXAmPLl2W>DnFqzyum_0{&ctgWE!o*(9Oxr4ZKzaK02U&x_i8#5zfqeqz*O{`Xu9HO zh(R_&KljX7?3Skwv;->!Vm(yD2alE?A$o7&=}L=!jreas^ZO4-AJ6*F401ynJ5gHICWebJHLClt(zVhL@833ZU0)hyqgz1rCmcPLNjV z94*xHEVVBwT2a;6#LuH}eaHT-2@}*ig)uGlOWNG#U*`JQ|S=U04CPT3t zV)MbnT=zW*g1G@>&+)_`WC|_=AS{QaS-vw)I|@mj&>ZoBSfxO2$(ySfw(a4$8(2BO z!K6+j<$tw_aTMS&ji>>V+QjSM7{zNNvd@62=vh9wFvc^-53n%!L0Zan{h4-SX*bgEWR?-j~_RJ3j5iW+<_l#baAX zwuifb%5*9SFyLFoNiXYp)ZeJ1;_RkJUUY2R)&a5clQ`a*xQuESwWGnFV2GL@>@0yS zrm5b@`nr6Q_w%H)XI;KooVE5WS88kT(SA)X#LS>;DWh(I{|>b@ zyq;>*eRm!`n7Nb#YdOy=#;BN$JEb&UH{+wd1Qt4Vp0bjj#&w-w`UmGpw(zl1I*Q`7 zWD7I^XbGL~yqV#))rg3nX>^Srq?f-5)42Nj+mlM!zrfv_ahFv)nFUBJ9vBfMEsuM+ z6kc<67l=9)nyfZh9p34z+jY!QwulS3Olofhh$}{>C2Fu}16C)5TqWy!4-%{3-2E%H zyS3ZCo<4&3q^=Gut?v>6k1CgbAlPf4Y<{781&wq1W9}Hs=N9L@S%SJ!! z-a9Vde&xK8=`!~0*I0Agk3;oIE24h?Gb*e#VnFSN@ExG$;J!spJA8fn8^u>iYBRod z+rX-FjHd%>GZlUX*&2fE{rcY5%5A2^V8|LIcBP%DkvkCekX15r@ zdh}Jj-*ial6v7q^7(7_yP8*<#3EXf=z{7ilNne9>CfTv=7IA^zd!)5|#lyBcrluA1 z9$#*OF5GTXg!osc2GdCG?gq&lTQdbho%!nof5bS%7=7Py`^SH*wMtL>;KW2_HS+hr zN!(m&^z?3LncE&c0GY4$B;gCPHvQou0Lz>)n;lOWow={TRpxM!uLWHdNLzVjYe06o zEx3Bj_qXt7U!WNYlq#|lJHo#MXQR&Fg7l8eC$kukNt3oU)i0u9gxt8m6|4XF)H4`} zHC`SBT8eNb4ioN}4mubl`Zza;*H-9>Y%E$Fvq8~`Ta@kr3|s4VFzv-cs#b69`+n9K z0TjdUg8_cv+G`J7jzFgZpM>I^w4|r)2d)U!kYGP*5xmCedyay^IzSF+ zKOD0wPx4}=in{7jyB7;ge%cpTS^#;G5~aY#W(fIL21%|DV0BP1+~6#jvzr{s+m&JR zNdXg}*AC5i3wEuroI126N7~)nmvOZa5gdPzDIxw%J=*N@Vr{YLK9+P%bfsCrh^Q!1 z*Y2eEQ~@v}JEcRjIDw+|S9IFD6o*SM_FMp6Pe=t@zc{iTpeGCo6Pk|DyBf zN|C0I1DX@e`5R-Y7-|F0n0Fznz_vzk#9)u`^5_~u&|gJx8JoXiZNA;&{;VqCH{J@s z4H4+-CDIw~Sv&_a;Nn9O+l1)+eGjk<>-8oVdAo2_NAOEIW?0gjZHrF~Of!33U+fRM zDG7lGWIhwZpI4Sh>PiOh)e@zZ9~BQ`sZ>QE*FkQ^r=mB;ycq18hKn1XPc+}4XWB`M z)ehA2iKTgLX3W6$a4yiJ@Y0zBXT?%6eX}qK*YKoRA=ziND&K55-)z>owpwl9Ktc0s z!;hF=!7^escdk`Wj9hi{fj~#u`!^~tbQ-2s8lnv~gY5P+muxe>257Yetl~<~-&&0n z+00e|0=sXfa;w9UFcwjxhhs16MN7hL5}=w(%RZxMYyEEuGc`TaHG;Y6k>_=xCz|n` zk0P;EAZ#4hEj^-0>ul5cnb~1$p@ZZd%<9)zzd+mapNY7w%mE6TBR*Lg{f!>g?zkN- zTvIxpV;HOc$KBdWP9z5Qtgqo(Zm+%f|IW#j2+J4L%luDPCbLz`RUH*ZtwEQ0ySx_C z+&nwctz+vs`@?4ym2e&~)`PNy+s_+RP+x&m$(pE8-3AGYe^mO7m~xnuz@-j%s%ds~ zRqu9`Q7G?V;7w~6wEz0t=4SbieZ{im^7VXVld=C@4gd%)H%(~2E$~=ErKu*y-h*F~ zz1T0rZWdUUrv6cg)6B%;LuE3bkEndPiUMkDUv>Ju-?UawAg8-|_;nxK_Rftl&7jwF z>FMMo>?2wn)n=*E-|EpJju&IFz5EIwZfT!cHg#&ow?wGs-6=Ca8LZm&aa+M7<5K46 z$)3)VL^IDq{CD{h&i-p&f-(g{sP15lE1)ni za8Hm^@+F3ix)fV^5;z348oMpq(Z(~QWhpWr%^ai6*4bh*>y{285&8dGreCSFE|)EW zCCXkKx^@%R)T&(*{V_iOhHAEGE3s>j?EPO#d}0Fy<4&Qc^UxNkYYjfj-+Y>m`6^bV z_^?n~st!`+7{tIG)(vd@26grttFV_9q=v zB8Fr9KxO$1`lJ^C8du(5;v-Qpo}AT0I9$80dATaTJmqM2@DsbxzJ|{_SIMMhN}T2O zWXY(vVDj|oUi(y!H>lmWy&0GSALe^cigyLI$VzY6x1^2sEkmH;Wg(DhKX8i_BGM-t5>* zbel&aZChHWw8L>8CyTVzU_Zgkf1RB(@ri7q9I3Js2(e3 zKliHUG&UFERf4ouYN{^3BmzsD<0$LZ)UHB}*EktRqsEOC-};yjSAD96A~{6{>2=!2 z*#4f4*MD@+#aXJMfs2<(`g_YHKNvac7)g+aR5Wf|jNddG=f~^4&7bxBf^@GA##~Y5 zNI->0c2##jb}jMC?b~DN{GH>s?9t%)28N2*E9XqKfNzr0mOMm|y6#vccOBuy%e79ClLN#C8_@n=* znc^d7oZ6ozlU$$htGDjb{w+<{IYYC#25|wzDu<)>{R(?5s&Z@PxZDWZLKZvrU|ZSy zIK=OGFnnU+t?O%zF@e9T65f9i{S~iS3fW3J9BmPKF41_Cx9(r*u4lZ*Tg0-y(aU%_ zl>6ky%4h#vEKzgfo*=QIkgat%7`~yW>(_D52Fl~J#gaMEdk=7{;pbq|=O(X&A;4o^ zIL;EFT$dCUPt%vI14Sb@6c9>2=wm+hpvMvNpDViFKP%VRZ!yGonOFmOnBgp2QRQRy z;BAl6ay3A{UV2(qp5R?(+Dp`R;Kp)>a>s4y8>HP%4p{sGk?ujF-eLHg<6F~5!1q!P zqM88WQ{0a1gC4qUwilRIzBv31fjL)PLn@67M=lfwn!)m7qa86W$!1RJOrPg<1eR{+ zPmj#9X9}OdN9QF#7I-?E1c&T)#Akl4Q2@CXYw6d^cn6pdos{?&4DR4B>H>kLnd`ar z#VawZKE6Mi8f(nA9H-n#JZjffwiiqHE25;f@O)~telqpVdQ>t!@emzJ19u8)t>hlg zaernP9Yf3Bvhp>r>9G1B5KMd5!bdbv;GWcSB#!T2o=85A+xsbYisIsJ`m(cgbxjbj z_wu!qIKL2omj}E~(vLwCc2H2Z|E;E)nL>J2C*P5FNbFM9-dgSYAV}MJqZ7Ax0LOOt>3L1agjc!9f0K@CFlg%zl&7+6f z=my6Wm|up^S2Bl2bMcD! zIVKI*GV=dLSb)SuY9{!SO6TWbnRz)TF5Tu0tqQ(#V<_P z6VZ`l>$6{N`?N7dsATSW>8AlFm0n*&rKPJqaKd5S)mi;tN=PHQK5NJ60ypq!O(7Q> zjw_3QNeeQrCw8p)?a=plS!mD|xu_IOCT51Q@Z-VH9dXaHEHk@{QDt>Xk7SaKAKD~> zRd0afaRc*@nq(D76ZL9hE%L=pt9?Kp69}~Sz2MCM(WeEN+5e-J3qX|rpN_8}cA#ZW zI_@NQAY}HV(Cv1+Gv&c0nLvu~F5=lxdY-BJ0?=3#DRsscfioY^XMa3<;bB2AgX=Xf zgHX=9jDQMRzxaiLf*GV^&jJGztU=j)8%cI@!MsA$6o&-^x&VQN0>%~BQ4x^> zQMX}@X#V2xNM_y7oB(#+YY#dQ|HQ_z`{Q&6aD_LMbC96iPdQ9^YCD6XH)N=9eGKK> z@y-8jf(m4~Phi^1|i| zVGrdv3N|hh0Zp_rB*wYhFQ?3Oe@T>HEMW9VkUY^|WjBrK)(kJt--SaQ&>sg io.helidon.examples.integrations.oci.objecstorage.reactive.OciObjectStorageMain - + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + io.helidon.webserver helidon-webserver - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage + io.helidon.config + helidon-config-yaml - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage-health + com.oracle.oci.sdk + oci-java-sdk-shaded-full - io.helidon.config - helidon-config-yaml + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime diff --git a/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/ObjectStorageService.java b/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/ObjectStorageService.java index 0541aea65a6..144abe7fa1f 100644 --- a/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/ObjectStorageService.java +++ b/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/ObjectStorageService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,29 +16,49 @@ package io.helidon.examples.integrations.oci.objecstorage.reactive; -import java.util.Optional; -import java.util.OptionalLong; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; -import io.helidon.common.http.DataChunk; import io.helidon.common.http.Http; -import io.helidon.integrations.oci.objectstorage.DeleteObject; -import io.helidon.integrations.oci.objectstorage.GetObject; -import io.helidon.integrations.oci.objectstorage.GetObjectRx; -import io.helidon.integrations.oci.objectstorage.OciObjectStorageRx; -import io.helidon.integrations.oci.objectstorage.PutObject; -import io.helidon.integrations.oci.objectstorage.RenameObject; import io.helidon.webserver.Routing; import io.helidon.webserver.ServerRequest; import io.helidon.webserver.ServerResponse; import io.helidon.webserver.Service; +import com.oracle.bmc.objectstorage.ObjectStorageAsync; +import com.oracle.bmc.objectstorage.model.RenameObjectDetails; +import com.oracle.bmc.objectstorage.requests.DeleteObjectRequest; +import com.oracle.bmc.objectstorage.requests.GetNamespaceRequest; +import com.oracle.bmc.objectstorage.requests.GetObjectRequest; +import com.oracle.bmc.objectstorage.requests.PutObjectRequest; +import com.oracle.bmc.objectstorage.requests.RenameObjectRequest; +import com.oracle.bmc.objectstorage.responses.DeleteObjectResponse; +import com.oracle.bmc.objectstorage.responses.GetNamespaceResponse; +import com.oracle.bmc.objectstorage.responses.GetObjectResponse; +import com.oracle.bmc.objectstorage.responses.PutObjectResponse; +import com.oracle.bmc.objectstorage.responses.RenameObjectResponse; +import com.oracle.bmc.responses.AsyncHandler; + class ObjectStorageService implements Service { - private final OciObjectStorageRx objectStorage; + private static final Logger LOGGER = Logger.getLogger(ObjectStorageService.class.getName()); + private final ObjectStorageAsync objectStorageAsyncClient; private final String bucketName; + private final String namespaceName; - ObjectStorageService(OciObjectStorageRx objectStorage, String bucketName) { - this.objectStorage = objectStorage; + ObjectStorageService(ObjectStorageAsync objectStorageAsyncClient, String bucketName) throws Exception { + this.objectStorageAsyncClient = objectStorageAsyncClient; this.bucketName = bucketName; + ResponseHandler namespaceHandler = + new ResponseHandler<>(); + this.objectStorageAsyncClient.getNamespace(GetNamespaceRequest.builder().build(), namespaceHandler); + GetNamespaceResponse namespaceResponse = namespaceHandler.waitForCompletion(); + this.namespaceName = namespaceResponse.getValue(); } @Override @@ -52,67 +72,152 @@ public void update(Routing.Rules rules) { private void delete(ServerRequest req, ServerResponse res) { String objectName = req.path().param("file-name"); - objectStorage.deleteObject(DeleteObject.Request.builder() - .bucket(bucketName) - .objectName(objectName)) - .forSingle(response -> res.status(response.status()).send()) - .exceptionally(res::send); + ResponseHandler deleteObjectHandler = + new ResponseHandler<>(); + + objectStorageAsyncClient.deleteObject(DeleteObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(objectName).build(), deleteObjectHandler); + try { + DeleteObjectResponse deleteObjectResponse = deleteObjectHandler.waitForCompletion(); + res.status(Http.Status.OK_200) + .send(); + return; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error deleting object", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } } private void rename(ServerRequest req, ServerResponse res) { String oldName = req.path().param("old-name"); String newName = req.path().param("new-name"); - objectStorage.renameObject(RenameObject.Request.builder() - .bucket(bucketName) - .objectName(oldName) - .newObjectName(newName)) - .forSingle(it -> res.send("Renamed to " + newName)) - .exceptionally(res::send); + RenameObjectRequest renameObjectRequest = RenameObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .renameObjectDetails(RenameObjectDetails.builder() + .newName(newName) + .sourceName(oldName) + .build()) + .build(); + + ResponseHandler renameObjectHandler = + new ResponseHandler<>(); + + try { + objectStorageAsyncClient.renameObject(renameObjectRequest, renameObjectHandler); + RenameObjectResponse renameObjectResponse = renameObjectHandler.waitForCompletion(); + res.status(Http.Status.OK_200) + .send(); + return; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error renaming object", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } } private void upload(ServerRequest req, ServerResponse res) { - OptionalLong contentLength = req.headers().contentLength(); - if (contentLength.isEmpty()) { - req.content().forEach(DataChunk::release); - res.status(Http.Status.BAD_REQUEST_400).send("Content length must be defined"); + String objectName = req.path().param("file-name"); + PutObjectRequest putObjectRequest = null; + try (InputStream stream = new FileInputStream(System.getProperty("user.dir") + File.separator + objectName)) { + byte[] contents = stream.readAllBytes(); + putObjectRequest = + PutObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(objectName) + .putObjectBody(new ByteArrayInputStream(contents)) + .contentLength(Long.valueOf(contents.length)) + .build(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error creating PutObjectRequest", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); return; } - String objectName = req.path().param("file-name"); - - PutObject.Request request = PutObject.Request.builder() - .objectName(objectName) - .bucket(bucketName) - .contentLength(contentLength.getAsLong()); + ResponseHandler putObjectHandler = + new ResponseHandler<>(); - req.headers().contentType().ifPresent(request::requestMediaType); - - objectStorage.putObject(request, - req.content()) - .forSingle(response -> res.send(response.requestId())) - .exceptionally(res::send); + try { + objectStorageAsyncClient.putObject(putObjectRequest, putObjectHandler); + PutObjectResponse putObjectResponse = putObjectHandler.waitForCompletion(); + res.status(Http.Status.OK_200).send(); + return; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error uploading object", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } } private void download(ServerRequest req, ServerResponse res) { String objectName = req.path().param("file-name"); + ResponseHandler objectHandler = + new ResponseHandler<>(); + GetObjectRequest getObjectRequest = + GetObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(objectName) + .build(); + GetObjectResponse getObjectResponse = null; + try { + objectStorageAsyncClient.getObject(getObjectRequest, objectHandler); + getObjectResponse = objectHandler.waitForCompletion(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error getting object", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } + + if (getObjectResponse.getContentLength() == 0) { + LOGGER.log(Level.SEVERE, "GetObjectResponse is empty"); + res.status(Http.Status.NOT_FOUND_404).send(); + return; + } + + try (InputStream fileStream = getObjectResponse.getInputStream()) { + byte[] objectContent = fileStream.readAllBytes(); + res.addHeader(Http.Header.CONTENT_DISPOSITION, "attachment; filename=\"" + objectName + "\"") + .status(Http.Status.OK_200).send(objectContent); + return; + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error processing GetObjectResponse", e); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(); + return; + } + } + + private static class ResponseHandler implements AsyncHandler { + private OUT item; + private Throwable failed = null; + private CountDownLatch latch = new CountDownLatch(1); + + private OUT waitForCompletion() throws Exception { + latch.await(); + if (failed != null) { + if (failed instanceof Exception) { + throw (Exception) failed; + } + throw (Error) failed; + } + return item; + } - objectStorage.getObject(GetObject.Request.builder() - .bucket(bucketName) - .objectName(objectName)) - .forSingle(apiResponse -> { - Optional entity = apiResponse.entity(); - if (entity.isEmpty()) { - res.status(Http.Status.NOT_FOUND_404).send(); - } else { - GetObjectRx.Response response = entity.get(); - // copy the content length header to response - apiResponse.headers() - .first(Http.Header.CONTENT_LENGTH) - .ifPresent(res.headers()::add); - res.send(response.publisher()); - } - }) - .exceptionally(res::send); + @Override + public void onSuccess(IN request, OUT response) { + item = response; + latch.countDown(); + } + + @Override + public void onError(IN request, Throwable error) { + failed = error; + latch.countDown(); + } } } diff --git a/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/OciObjectStorageMain.java b/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/OciObjectStorageMain.java index 772bfca7b3c..81be8ef9bb4 100644 --- a/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/OciObjectStorageMain.java +++ b/examples/integrations/oci/objectstorage-reactive/src/main/java/io/helidon/examples/integrations/oci/objecstorage/reactive/OciObjectStorageMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,12 +18,16 @@ import io.helidon.common.LogConfig; import io.helidon.config.Config; -import io.helidon.health.HealthSupport; -import io.helidon.integrations.oci.objectstorage.OciObjectStorageRx; -import io.helidon.integrations.oci.objectstorage.health.OciObjectStorageHealthCheck; import io.helidon.webserver.Routing; import io.helidon.webserver.WebServer; +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.AuthenticationDetailsProvider; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.model.BmcException; +import com.oracle.bmc.objectstorage.ObjectStorageAsync; +import com.oracle.bmc.objectstorage.ObjectStorageAsyncClient; + import static io.helidon.config.ConfigSources.classpath; import static io.helidon.config.ConfigSources.file; @@ -40,7 +44,7 @@ private OciObjectStorageMain() { * * @param args ignored */ - public static void main(String[] args) { + public static void main(String[] args) throws Exception { LogConfig.configureRuntime(); // as I cannot share my configuration of OCI, let's combine the configuration // from my home directory with the one compiled into the jar @@ -52,26 +56,19 @@ public static void main(String[] args) { // this requires OCI configuration in the usual place // ~/.oci/config - OciObjectStorageRx ociObjectStorage = OciObjectStorageRx.create(ociConfig); + AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(ConfigFileReader.parseDefault()); + ObjectStorageAsync objectStorageAsyncClient = new ObjectStorageAsyncClient(authProvider); // the following parameters are required - String bucketName = ociConfig.get("objectstorage").get("bucket").asString().get(); - String namespace = ociConfig.get("objectstorage").get("namespace").asString().get(); - - // setup ObjectStorage health check - HealthSupport health = HealthSupport.builder() - .addLiveness(OciObjectStorageHealthCheck.builder() - .ociObjectStorage(ociObjectStorage) - .addBucket(bucketName) - .namespace(namespace) - .build()) - .build(); + String bucketName = ociConfig.get("objectstorage").get("bucketName").asString().get(); WebServer.builder() .config(config.get("server")) .routing(Routing.builder() - .register(health) - .register("/files", new ObjectStorageService(ociObjectStorage, bucketName))) + .register("/files", new ObjectStorageService(objectStorageAsyncClient, bucketName)) + // OCI SDK error handling + .error(BmcException.class, (req, res, ex) -> res.status(ex.getStatusCode()) + .send(ex.getMessage()))) .build() .start() .await(); diff --git a/examples/integrations/oci/objectstorage-reactive/src/main/java/module-info.java b/examples/integrations/oci/objectstorage-reactive/src/main/java/module-info.java index dff47855ffe..9f563a23f7e 100644 --- a/examples/integrations/oci/objectstorage-reactive/src/main/java/module-info.java +++ b/examples/integrations/oci/objectstorage-reactive/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,11 +18,12 @@ * Example of integration with OCI object storage in reactive application. */ module io.helidon.examples.integrations.oci.objectstorage.reactive { + requires java.logging; + requires io.helidon.common.http; - requires io.helidon.integrations.oci.objectstorage; - requires io.helidon.integrations.oci.objectstorage.health; requires io.helidon.webserver; - requires io.helidon.health; + + requires oci.java.sdk.shaded.full; exports io.helidon.examples.integrations.oci.objecstorage.reactive; } \ No newline at end of file diff --git a/examples/integrations/oci/objectstorage-reactive/src/main/resources/application.yaml b/examples/integrations/oci/objectstorage-reactive/src/main/resources/application.yaml index 4ca04b175ed..4c07c154bc6 100644 --- a/examples/integrations/oci/objectstorage-reactive/src/main/resources/application.yaml +++ b/examples/integrations/oci/objectstorage-reactive/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -23,5 +23,4 @@ server: oci: objectstorage: - namespace: "${oci.properties.objectstorage-namespace}" - bucket: "${oci.properties.objectstorage-bucket}" + bucketName: "${oci.properties.objectstorage-bucketName}" diff --git a/examples/integrations/oci/pom.xml b/examples/integrations/oci/pom.xml index e61dd4e4b66..f8a6f036187 100644 --- a/examples/integrations/oci/pom.xml +++ b/examples/integrations/oci/pom.xml @@ -32,11 +32,19 @@ Helidon Examples Integration OCI Examples of integration with OCI (Oracle Cloud). - - metrics-reactive - objectstorage-reactive - objectstorage-cdi - vault-reactive - vault-cdi - + + + oci-sdk-cdi + + atp-reactive + atp-cdi + metrics-reactive + objectstorage-reactive + objectstorage-cdi + vault-reactive + vault-cdi + + + + diff --git a/examples/integrations/oci/vault-cdi/pom.xml b/examples/integrations/oci/vault-cdi/pom.xml index bec1593b644..cd298a240e3 100644 --- a/examples/integrations/oci/vault-cdi/pom.xml +++ b/examples/integrations/oci/vault-cdi/pom.xml @@ -37,26 +37,59 @@ io.helidon.examples.integrations.oci.vault.cdi.VaultCdiMain + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.microprofile.bundles helidon-microprofile - io.helidon.integrations.oci - helidon-integrations-oci-cdi + io.helidon.integrations.oci.sdk + helidon-integrations-oci-sdk-cdi + runtime + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + + + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime - io.helidon.integrations.oci - helidon-integrations-oci-vault + org.junit.jupiter + junit-jupiter-api + test - io.helidon.integrations.oci - helidon-integrations-oci-vault-health + org.hamcrest + hamcrest-all + test - io.helidon.config - helidon-config-yaml-mp + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test diff --git a/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/CryptoClientProducer.java b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/CryptoClientProducer.java new file mode 100644 index 00000000000..5050eb59b09 --- /dev/null +++ b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/CryptoClientProducer.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.helidon.examples.integrations.oci.vault.cdi; + +import com.oracle.bmc.keymanagement.KmsCryptoClient; +import com.oracle.bmc.keymanagement.KmsCryptoClientBuilder; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * KMS crypto client (used for encryption, decryption and signatures) requires additional configuration, that cannot + * be done automatically by the SDK. + */ +@ApplicationScoped +class CryptoClientProducer { + private final String cryptoEndpoint; + + @Inject + CryptoClientProducer(@ConfigProperty(name = "app.vault.cryptographic-endpoint") + String cryptoEndpoint) { + this.cryptoEndpoint = cryptoEndpoint; + } + + @Produces + KmsCryptoClientBuilder clientBuilder() { + return KmsCryptoClient.builder() + .endpoint(cryptoEndpoint); + } +} diff --git a/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/ErrorHandlerProvider.java b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/ErrorHandlerProvider.java new file mode 100644 index 00000000000..a6b67e6ff22 --- /dev/null +++ b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/ErrorHandlerProvider.java @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package io.helidon.examples.integrations.oci.vault.cdi; + +import com.oracle.bmc.model.BmcException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * Maps SDK errors to HTTP errors, as otherwise any exception is manifested as an internal server error. + * This mapper is not part of integration with OCI SDK, as each application may require a different entity format. + * This mapper simply uses the response code as HTTP status code, and error message as entity. + */ +@Provider +class ErrorHandlerProvider implements ExceptionMapper { + @Override + public Response toResponse(BmcException e) { + return Response.status(e.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/VaultResource.java b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/VaultResource.java index d0047b0d903..fdeac30e50b 100644 --- a/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/VaultResource.java +++ b/examples/integrations/oci/vault-cdi/src/main/java/io/helidon/examples/integrations/oci/vault/cdi/VaultResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,23 +18,34 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Optional; +import java.util.Date; import io.helidon.common.Base64Value; -import io.helidon.integrations.oci.vault.CreateSecret; -import io.helidon.integrations.oci.vault.Decrypt; -import io.helidon.integrations.oci.vault.DeleteSecret; -import io.helidon.integrations.oci.vault.Encrypt; -import io.helidon.integrations.oci.vault.GetSecretBundle; -import io.helidon.integrations.oci.vault.OciVault; -import io.helidon.integrations.oci.vault.Sign; -import io.helidon.integrations.oci.vault.Verify; +import com.oracle.bmc.keymanagement.KmsCrypto; +import com.oracle.bmc.keymanagement.model.DecryptDataDetails; +import com.oracle.bmc.keymanagement.model.EncryptDataDetails; +import com.oracle.bmc.keymanagement.model.SignDataDetails; +import com.oracle.bmc.keymanagement.model.VerifyDataDetails; +import com.oracle.bmc.keymanagement.requests.DecryptRequest; +import com.oracle.bmc.keymanagement.requests.EncryptRequest; +import com.oracle.bmc.keymanagement.requests.SignRequest; +import com.oracle.bmc.keymanagement.requests.VerifyRequest; +import com.oracle.bmc.secrets.Secrets; +import com.oracle.bmc.secrets.model.Base64SecretBundleContentDetails; +import com.oracle.bmc.secrets.model.SecretBundleContentDetails; +import com.oracle.bmc.secrets.requests.GetSecretBundleRequest; +import com.oracle.bmc.vault.Vaults; +import com.oracle.bmc.vault.model.Base64SecretContentDetails; +import com.oracle.bmc.vault.model.CreateSecretDetails; +import com.oracle.bmc.vault.model.ScheduleSecretDeletionDetails; +import com.oracle.bmc.vault.model.SecretContentDetails; +import com.oracle.bmc.vault.requests.CreateSecretRequest; +import com.oracle.bmc.vault.requests.ScheduleSecretDeletionRequest; import jakarta.inject.Inject; -import jakarta.inject.Named; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; -import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -45,23 +56,29 @@ */ @Path("/vault") public class VaultResource { - private final OciVault vault; + private final Secrets secrets; + private final KmsCrypto crypto; + private final Vaults vaults; private final String vaultOcid; private final String compartmentOcid; private final String encryptionKeyOcid; private final String signatureKeyOcid; @Inject - VaultResource(@Named("custom") OciVault vault, + VaultResource(Secrets secrets, + KmsCrypto crypto, + Vaults vaults, @ConfigProperty(name = "app.vault.vault-ocid") - String vaultOcid, + String vaultOcid, @ConfigProperty(name = "app.vault.compartment-ocid") - String compartmentOcid, + String compartmentOcid, @ConfigProperty(name = "app.vault.encryption-key-ocid") - String encryptionKeyOcid, + String encryptionKeyOcid, @ConfigProperty(name = "app.vault.signature-key-ocid") - String signatureKeyOcid) { - this.vault = vault; + String signatureKeyOcid) { + this.secrets = secrets; + this.crypto = crypto; + this.vaults = vaults; this.vaultOcid = vaultOcid; this.compartmentOcid = compartmentOcid; this.encryptionKeyOcid = encryptionKeyOcid; @@ -77,10 +94,14 @@ public class VaultResource { @GET @Path("/encrypt/{text}") public String encrypt(@PathParam("text") String secret) { - return vault.encrypt(Encrypt.Request.builder() - .keyId(encryptionKeyOcid) - .data(Base64Value.create(secret))) - .cipherText(); + return crypto.encrypt(EncryptRequest.builder() + .encryptDataDetails(EncryptDataDetails.builder() + .keyId(encryptionKeyOcid) + .plaintext(Base64Value.create(secret).toBase64()) + .build()) + .build()) + .getEncryptedData() + .getCiphertext(); } /** @@ -92,10 +113,14 @@ public String encrypt(@PathParam("text") String secret) { @GET @Path("/decrypt/{text: .*}") public String decrypt(@PathParam("text") String cipherText) { - return vault.decrypt(Decrypt.Request.builder() - .keyId(encryptionKeyOcid) - .cipherText(cipherText)) - .decrypted() + return Base64Value.createFromEncoded(crypto.decrypt(DecryptRequest.builder() + .decryptDataDetails(DecryptDataDetails.builder() + .keyId(encryptionKeyOcid) + .ciphertext(cipherText) + .build()) + .build()) + .getDecryptedData() + .getPlaintext()) .toDecodedString(); } @@ -108,31 +133,40 @@ public String decrypt(@PathParam("text") String cipherText) { @GET @Path("/sign/{text}") public String sign(@PathParam("text") String dataToSign) { - return vault.sign(Sign.Request.builder() - .keyId(signatureKeyOcid) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .message(Base64Value.create(dataToSign))) - .signature() - .toBase64(); + return crypto.sign(SignRequest.builder() + .signDataDetails(SignDataDetails.builder() + .keyId(signatureKeyOcid) + .signingAlgorithm(SignDataDetails.SigningAlgorithm.Sha224RsaPkcsPss) + .message(Base64Value.create(dataToSign).toBase64()) + .build()) + .build()) + .getSignedData() + .getSignature(); } /** - * Verify a signature. + * Verify a signature. The base64 encoded signature is the entity * * @param dataToVerify data that was signed - * @param signature signature text + * @param signature signature text * @return whether the signature is valid or not */ - @GET - @Path("/sign/{text}/{signature: .*}") + @POST + @Path("/verify/{text}") public String verify(@PathParam("text") String dataToVerify, - @PathParam("signature") String signature) { - boolean valid = vault.verify(Verify.Request.builder() - .keyId(signatureKeyOcid) - .message(Base64Value.create(dataToVerify)) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .signature(Base64Value.createFromEncoded(signature))) - .isValid(); + String signature) { + VerifyDataDetails.SigningAlgorithm algorithm = VerifyDataDetails.SigningAlgorithm.Sha224RsaPkcsPss; + + boolean valid = crypto.verify(VerifyRequest.builder() + .verifyDataDetails(VerifyDataDetails.builder() + .keyId(signatureKeyOcid) + .signingAlgorithm(algorithm) + .message(Base64Value.create(dataToVerify).toBase64()) + .signature(signature) + .build()) + .build()) + .getVerifiedData() + .getIsSignatureValid(); return valid ? "Signature valid" : "Signature not valid"; } @@ -146,15 +180,18 @@ public String verify(@PathParam("text") String dataToVerify, @GET @Path("/secret/{id}") public String getSecret(@PathParam("id") String secretOcid) { - Optional response = vault.getSecretBundle(GetSecretBundle.Request.builder() - .secretId(secretOcid)) - .entity(); - - if (response.isEmpty()) { - throw new NotFoundException("Secret with id " + secretOcid + " does not exist"); + SecretBundleContentDetails content = secrets.getSecretBundle(GetSecretBundleRequest.builder() + .secretId(secretOcid) + .build()) + .getSecretBundle() + .getSecretBundleContent(); + + if (content instanceof Base64SecretBundleContentDetails) { + // the only supported type + return Base64Value.createFromEncoded(((Base64SecretBundleContentDetails) content).getContent()).toDecodedString(); + } else { + throw new InternalServerErrorException("Invalid secret content type"); } - - return response.get().secretString().orElse(""); } /** @@ -168,19 +205,22 @@ public String getSecret(@PathParam("id") String secretOcid) { @Path("/secret/{id}") public String deleteSecret(@PathParam("id") String secretOcid) { // has to be for quite a long period of time - did not work with less than 30 days - Instant deleteTime = Instant.now().plus(30, ChronoUnit.DAYS); + Date deleteTime = Date.from(Instant.now().plus(30, ChronoUnit.DAYS)); - vault.deleteSecret(DeleteSecret.Request.builder() - .secretId(secretOcid) - .timeOfDeletion(deleteTime)); + vaults.scheduleSecretDeletion(ScheduleSecretDeletionRequest.builder() + .secretId(secretOcid) + .scheduleSecretDeletionDetails(ScheduleSecretDeletionDetails.builder() + .timeOfDeletion(deleteTime) + .build()) + .build()); - return "Secret " + secretOcid + " was deleted"; + return "Secret " + secretOcid + " was marked for deletion"; } /** * Create a new secret. * - * @param name name of the secret + * @param name name of the secret * @param secretText secret content * @return OCID of the created secret */ @@ -188,14 +228,20 @@ public String deleteSecret(@PathParam("id") String secretOcid) { @Path("/secret/{name}") public String createSecret(@PathParam("name") String name, String secretText) { - return vault.createSecret(CreateSecret.Request.builder() - .secretName(name) - .secretContent(CreateSecret.SecretContent.create(secretText)) - .vaultId(vaultOcid) - .compartmentId(compartmentOcid) - .encryptionKeyId(encryptionKeyOcid)) - .secret() - .id(); - + SecretContentDetails content = Base64SecretContentDetails.builder() + .content(Base64Value.create(secretText).toBase64()) + .build(); + + return vaults.createSecret(CreateSecretRequest.builder() + .createSecretDetails(CreateSecretDetails.builder() + .secretName(name) + .vaultId(vaultOcid) + .compartmentId(compartmentOcid) + .keyId(encryptionKeyOcid) + .secretContent(content) + .build()) + .build()) + .getSecret() + .getId(); } } diff --git a/examples/integrations/oci/vault-cdi/src/main/java/module-info.java b/examples/integrations/oci/vault-cdi/src/main/java/module-info.java index 13d50da12e6..b335352c671 100644 --- a/examples/integrations/oci/vault-cdi/src/main/java/module-info.java +++ b/examples/integrations/oci/vault-cdi/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,18 +18,15 @@ * Example of OCI Vault integration in CDI. */ module io.helidon.examples.integrations.oci.vault.cdi { - requires jakarta.json.bind; requires jakarta.ws.rs; - requires jakarta.inject; requires microprofile.config.api; requires io.helidon.config.yaml.mp; - requires io.helidon.integrations.oci.vault; requires io.helidon.microprofile.cdi; - exports io.helidon.examples.integrations.oci.vault.cdi; + requires oci.java.sdk.shaded.full; opens io.helidon.examples.integrations.oci.vault.cdi to weld.core.impl, io.helidon.microprofile.cdi; } \ No newline at end of file diff --git a/examples/integrations/oci/vault-cdi/src/main/resources/application.yaml b/examples/integrations/oci/vault-cdi/src/main/resources/application.yaml index cb7aca594f5..3ea5b75de39 100644 --- a/examples/integrations/oci/vault-cdi/src/main/resources/application.yaml +++ b/examples/integrations/oci/vault-cdi/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -14,15 +14,12 @@ # limitations under the License. # -# -# The values under oci.properties are read from ~/helidon/conf/examples.yaml -# - server: port: 8080 # -# The following properties under app are accessed by the application +# The following properties under oci are accessed by Helidon. Values +# under oci.properties.* are read from ~/helidon/conf/examples.yaml. # app: vault: @@ -31,14 +28,4 @@ app: compartment-ocid: "${oci.properties.compartment-ocid}" encryption-key-ocid: "${oci.properties.vault-key-ocid}" signature-key-ocid: "${oci.properties.vault-rsa-key-ocid}" - -# -# The following properties under oci are accessed by Helidon -# -oci: - vault: - healthchecks: [ "${oci.properties.vault-ocid}" ] - custom: - vault: - # named OCI configuration - cryptographic-endpoint: "${oci.properties.cryptographic-endpoint}" + cryptographic-endpoint: "${oci.properties.cryptographic-endpoint}" diff --git a/examples/integrations/oci/vault-reactive/pom.xml b/examples/integrations/oci/vault-reactive/pom.xml index d90bd667581..9c9510d86a5 100644 --- a/examples/integrations/oci/vault-reactive/pom.xml +++ b/examples/integrations/oci/vault-reactive/pom.xml @@ -37,22 +37,43 @@ io.helidon.examples.integrations.oci.vault.reactive.OciVaultMain + + + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + ${version.lib.oci} + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + io.helidon.webserver helidon-webserver - - io.helidon.integrations.oci - helidon-integrations-oci-vault - io.helidon.config helidon-config-yaml - io.helidon.integrations.oci - helidon-integrations-oci-vault-health + com.oracle.oci.sdk + oci-java-sdk-shaded-full + + + org.slf4j + slf4j-jdk14 + runtime + + + org.bouncycastle + bcpkix-jdk15on + runtime diff --git a/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciHandler.java b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciHandler.java new file mode 100644 index 00000000000..1eee9e8b5f2 --- /dev/null +++ b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciHandler.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package io.helidon.examples.integrations.oci.vault.reactive; + +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.oracle.bmc.responses.AsyncHandler; + +final class OciHandler { + private static final Logger LOGGER = Logger.getLogger(OciHandler.class.getName()); + + private OciHandler() { + } + + static AsyncHandler ociHandler(Consumer handler) { + return new AsyncHandler<>() { + @Override + public void onSuccess(REQ req, RES res) { + handler.accept(res); + } + + @Override + public void onError(REQ req, Throwable error) { + LOGGER.log(Level.WARNING, "OCI Exception", error); + if (error instanceof Error) { + throw (Error) error; + } + if (error instanceof RuntimeException) { + throw (RuntimeException) error; + } + throw new RuntimeException(error); + } + }; + } +} diff --git a/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciVaultMain.java b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciVaultMain.java index 9e2a273f623..d0c8cd586c4 100644 --- a/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciVaultMain.java +++ b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/OciVaultMain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -16,14 +16,24 @@ package io.helidon.examples.integrations.oci.vault.reactive; +import java.io.IOException; + import io.helidon.common.LogConfig; import io.helidon.config.Config; -import io.helidon.health.HealthSupport; -import io.helidon.integrations.oci.vault.OciVaultRx; -import io.helidon.integrations.oci.vault.health.OciVaultHealthCheck; import io.helidon.webserver.Routing; import io.helidon.webserver.WebServer; +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.AuthenticationDetailsProvider; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.keymanagement.KmsCryptoAsync; +import com.oracle.bmc.keymanagement.KmsCryptoAsyncClient; +import com.oracle.bmc.model.BmcException; +import com.oracle.bmc.secrets.SecretsAsync; +import com.oracle.bmc.secrets.SecretsAsyncClient; +import com.oracle.bmc.vault.VaultsAsync; +import com.oracle.bmc.vault.VaultsAsyncClient; + import static io.helidon.config.ConfigSources.classpath; import static io.helidon.config.ConfigSources.file; @@ -39,7 +49,7 @@ private OciVaultMain() { * Main method. * @param args ignored */ - public static void main(String[] args) { + public static void main(String[] args) throws IOException { LogConfig.configureRuntime(); // as I cannot share my configuration of OCI, let's combine the configuration @@ -54,28 +64,31 @@ public static void main(String[] args) { String compartmentOcid = vaultConfig.get("compartment-ocid").asString().get(); String encryptionKey = vaultConfig.get("encryption-key-ocid").asString().get(); String signatureKey = vaultConfig.get("signature-key-ocid").asString().get(); + String cryptoEndpoint = vaultConfig.get("cryptographic-endpoint").asString().get(); // this requires OCI configuration in the usual place // ~/.oci/config - OciVaultRx ociVault = OciVaultRx.create(config.get("oci")); + AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(ConfigFileReader.parseDefault()); - // setup vault health check - HealthSupport health = HealthSupport.builder() - .addLiveness(OciVaultHealthCheck.builder() - .addVaultId(vaultOcid) - .ociVault(ociVault) - .build()) - .build(); + SecretsAsync secrets = SecretsAsyncClient.builder().build(authProvider); + KmsCryptoAsync crypto = KmsCryptoAsyncClient.builder() + .endpoint(cryptoEndpoint) + .build(authProvider); + VaultsAsync vaults = VaultsAsyncClient.builder().build(authProvider); WebServer.builder() .config(config.get("server")) .routing(Routing.builder() - .register(health) - .register("/vault", new VaultService(ociVault, + .register("/vault", new VaultService(secrets, + vaults, + crypto, vaultOcid, compartmentOcid, encryptionKey, - signatureKey))) + signatureKey)) + // OCI SDK error handling + .error(BmcException.class, (req, res, ex) -> res.status(ex.getStatusCode()) + .send(ex.getMessage()))) .build() .start() .await(); diff --git a/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/VaultService.java b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/VaultService.java index d7963040663..061ccb05819 100644 --- a/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/VaultService.java +++ b/examples/integrations/oci/vault-reactive/src/main/java/io/helidon/examples/integrations/oci/vault/reactive/VaultService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,38 +18,57 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Optional; +import java.util.Date; import io.helidon.common.Base64Value; -import io.helidon.common.http.Http; -import io.helidon.integrations.oci.vault.CreateSecret; -import io.helidon.integrations.oci.vault.Decrypt; -import io.helidon.integrations.oci.vault.DeleteSecret; -import io.helidon.integrations.oci.vault.Encrypt; -import io.helidon.integrations.oci.vault.GetSecretBundle; -import io.helidon.integrations.oci.vault.OciVaultRx; -import io.helidon.integrations.oci.vault.Secret; -import io.helidon.integrations.oci.vault.Sign; -import io.helidon.integrations.oci.vault.Verify; import io.helidon.webserver.Handler; import io.helidon.webserver.Routing; import io.helidon.webserver.ServerRequest; import io.helidon.webserver.ServerResponse; import io.helidon.webserver.Service; +import com.oracle.bmc.keymanagement.KmsCryptoAsync; +import com.oracle.bmc.keymanagement.model.DecryptDataDetails; +import com.oracle.bmc.keymanagement.model.EncryptDataDetails; +import com.oracle.bmc.keymanagement.model.SignDataDetails; +import com.oracle.bmc.keymanagement.model.VerifyDataDetails; +import com.oracle.bmc.keymanagement.requests.DecryptRequest; +import com.oracle.bmc.keymanagement.requests.EncryptRequest; +import com.oracle.bmc.keymanagement.requests.SignRequest; +import com.oracle.bmc.keymanagement.requests.VerifyRequest; +import com.oracle.bmc.secrets.SecretsAsync; +import com.oracle.bmc.secrets.model.Base64SecretBundleContentDetails; +import com.oracle.bmc.secrets.model.SecretBundleContentDetails; +import com.oracle.bmc.secrets.requests.GetSecretBundleRequest; +import com.oracle.bmc.vault.VaultsAsync; +import com.oracle.bmc.vault.model.Base64SecretContentDetails; +import com.oracle.bmc.vault.model.CreateSecretDetails; +import com.oracle.bmc.vault.model.ScheduleSecretDeletionDetails; +import com.oracle.bmc.vault.model.SecretContentDetails; +import com.oracle.bmc.vault.requests.CreateSecretRequest; +import com.oracle.bmc.vault.requests.ScheduleSecretDeletionRequest; + +import static io.helidon.examples.integrations.oci.vault.reactive.OciHandler.ociHandler; + class VaultService implements Service { - private final OciVaultRx vault; + private final SecretsAsync secrets; + private final VaultsAsync vaults; + private final KmsCryptoAsync crypto; private final String vaultOcid; private final String compartmentOcid; private final String encryptionKeyOcid; private final String signatureKeyOcid; - VaultService(OciVaultRx vault, + VaultService(SecretsAsync secrets, + VaultsAsync vaults, + KmsCryptoAsync crypto, String vaultOcid, String compartmentOcid, String encryptionKeyOcid, String signatureKeyOcid) { - this.vault = vault; + this.secrets = secrets; + this.vaults = vaults; + this.crypto = crypto; this.vaultOcid = vaultOcid; this.compartmentOcid = compartmentOcid; this.encryptionKeyOcid = encryptionKeyOcid; @@ -61,94 +80,105 @@ public void update(Routing.Rules rules) { rules.get("/encrypt/{text:.*}", this::encrypt) .get("/decrypt/{text:.*}", this::decrypt) .get("/sign/{text}", this::sign) - .get("/verify/{text}/{signature:.*}", this::verify) + .post("/verify/{text}", Handler.create(String.class, this::verify)) .get("/secret/{id}", this::getSecret) .post("/secret/{name}", Handler.create(String.class, this::createSecret)) .delete("/secret/{id}", this::deleteSecret); } private void getSecret(ServerRequest req, ServerResponse res) { - vault.getSecretBundle(GetSecretBundle.Request.create(req.path().param("id"))) - .forSingle(apiResponse -> { - Optional entity = apiResponse.entity(); - if (entity.isEmpty()) { - res.status(Http.Status.NOT_FOUND_404).send(); - } else { - GetSecretBundle.Response response = entity.get(); - res.send(response.secretString().orElse("")); - } - }) - .exceptionally(res::send); - + secrets.getSecretBundle(GetSecretBundleRequest.builder() + .secretId(req.path().param("id")) + .build(), ociHandler(ociRes -> { + SecretBundleContentDetails content = ociRes.getSecretBundle().getSecretBundleContent(); + if (content instanceof Base64SecretBundleContentDetails) { + // the only supported type + res.send(Base64Value.createFromEncoded(((Base64SecretBundleContentDetails) content).getContent()) + .toDecodedString()); + } else { + req.next(new Exception("Invalid secret content type")); + } + })); } private void deleteSecret(ServerRequest req, ServerResponse res) { // has to be for quite a long period of time - did not work with less than 30 days - Instant deleteTime = Instant.now().plus(30, ChronoUnit.DAYS); + Date deleteTime = Date.from(Instant.now().plus(30, ChronoUnit.DAYS)); - vault.deleteSecret(DeleteSecret.Request.builder() - .secretId(req.path().param("id")) - .timeOfDeletion(deleteTime)) - .forSingle(it -> res.status(it.status()).send()) - .exceptionally(res::send); + String secretOcid = req.path().param("id"); + vaults.scheduleSecretDeletion(ScheduleSecretDeletionRequest.builder() + .secretId(secretOcid) + .scheduleSecretDeletionDetails(ScheduleSecretDeletionDetails.builder() + .timeOfDeletion(deleteTime) + .build()) + .build(), ociHandler(ociRes -> res.send("Secret " + secretOcid + + " was marked for deletion"))); } private void createSecret(ServerRequest req, ServerResponse res, String secretText) { - vault.createSecret(CreateSecret.Request.builder() - .secretContent(CreateSecret.SecretContent.create(secretText)) - .vaultId(vaultOcid) - .compartmentId(compartmentOcid) - .encryptionKeyId(encryptionKeyOcid) - .secretName(req.path().param("name"))) - .map(CreateSecret.Response::secret) - .map(Secret::id) - .forSingle(res::send) - .exceptionally(res::send); + SecretContentDetails content = Base64SecretContentDetails.builder() + .content(Base64Value.create(secretText).toBase64()) + .build(); + + vaults.createSecret(CreateSecretRequest.builder() + .createSecretDetails(CreateSecretDetails.builder() + .secretName(req.path().param("name")) + .vaultId(vaultOcid) + .compartmentId(compartmentOcid) + .keyId(encryptionKeyOcid) + .secretContent(content) + .build()) + .build(), ociHandler(ociRes -> res.send(ociRes.getSecret().getId()))); } - private void verify(ServerRequest req, ServerResponse res) { + private void verify(ServerRequest req, ServerResponse res, String signature) { String text = req.path().param("text"); - String signature = req.path().param("signature"); - - vault.verify(Verify.Request.builder() - .keyId(signatureKeyOcid) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .message(Base64Value.create(text)) - .signature(Base64Value.createFromEncoded(signature))) - .map(Verify.Response::isValid) - .map(it -> it ? "Signature Valid" : "Signature Invalid") - .forSingle(res::send) - .exceptionally(res::send); + VerifyDataDetails.SigningAlgorithm algorithm = VerifyDataDetails.SigningAlgorithm.Sha224RsaPkcsPss; + + crypto.verify(VerifyRequest.builder() + .verifyDataDetails(VerifyDataDetails.builder() + .keyId(signatureKeyOcid) + .signingAlgorithm(algorithm) + .message(Base64Value.create(text).toBase64()) + .signature(signature) + .build()) + .build(), + ociHandler(ociRes -> { + boolean valid = ociRes.getVerifiedData() + .getIsSignatureValid(); + res.send(valid ? "Signature valid" : "Signature not valid"); + })); } private void sign(ServerRequest req, ServerResponse res) { - vault.sign(Sign.Request.builder() - .keyId(signatureKeyOcid) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .message(Base64Value.create(req.path().param("text")))) - .map(Sign.Response::signature) - .map(Base64Value::toBase64) - .forSingle(res::send) - .exceptionally(res::send); + crypto.sign(SignRequest.builder() + .signDataDetails(SignDataDetails.builder() + .keyId(signatureKeyOcid) + .signingAlgorithm(SignDataDetails.SigningAlgorithm.Sha224RsaPkcsPss) + .message(Base64Value.create(req.path().param("text")).toBase64()) + .build()) + .build(), ociHandler(ociRes -> res.send(ociRes.getSignedData() + .getSignature()))); } private void encrypt(ServerRequest req, ServerResponse res) { - vault.encrypt(Encrypt.Request.builder() - .keyId(encryptionKeyOcid) - .data(Base64Value.create(req.path().param("text")))) - .map(Encrypt.Response::cipherText) - .forSingle(res::send) - .exceptionally(res::send); + crypto.encrypt(EncryptRequest.builder() + .encryptDataDetails(EncryptDataDetails.builder() + .keyId(encryptionKeyOcid) + .plaintext(Base64Value.create(req.path().param("text")).toBase64()) + .build()) + .build(), ociHandler(ociRes -> res.send(ociRes.getEncryptedData().getCiphertext()))); } private void decrypt(ServerRequest req, ServerResponse res) { - vault.decrypt(Decrypt.Request.builder() - .keyId(encryptionKeyOcid) - .cipherText(req.path().param("text"))) - .map(Decrypt.Response::decrypted) - .map(Base64Value::toDecodedString) - .forSingle(res::send) - .exceptionally(res::send); + crypto.decrypt(DecryptRequest.builder() + .decryptDataDetails(DecryptDataDetails.builder() + .keyId(encryptionKeyOcid) + .ciphertext(req.path().param("text")) + .build()) + .build(), ociHandler(ociRes -> res.send(Base64Value.createFromEncoded(ociRes.getDecryptedData() + .getPlaintext()) + .toDecodedString()))); } } diff --git a/examples/integrations/oci/vault-reactive/src/main/java/module-info.java b/examples/integrations/oci/vault-reactive/src/main/java/module-info.java index 62881fe46a6..d97488da9e3 100644 --- a/examples/integrations/oci/vault-reactive/src/main/java/module-info.java +++ b/examples/integrations/oci/vault-reactive/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -18,10 +18,11 @@ * Example of OCI Vault integration in a reactive application. */ module io.helidon.examples.integrations.oci.vault.reactive { - requires io.helidon.integrations.oci.vault; - requires io.helidon.integrations.oci.vault.health; requires io.helidon.webserver; - requires io.helidon.health; + + requires oci.java.sdk.shaded.full; + + requires java.logging; exports io.helidon.examples.integrations.oci.vault.reactive; } \ No newline at end of file diff --git a/examples/integrations/oci/vault-reactive/src/main/resources/application.yaml b/examples/integrations/oci/vault-reactive/src/main/resources/application.yaml index 3d71eb108d8..982ef550c45 100644 --- a/examples/integrations/oci/vault-reactive/src/main/resources/application.yaml +++ b/examples/integrations/oci/vault-reactive/src/main/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2021, 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. @@ -14,13 +14,13 @@ # limitations under the License. # -# The values are read from -# ~/helidon/conf/examples.yaml -# or you can just update them here - server: port: 8080 +# +# The following properties under oci are accessed by Helidon. Values +# under oci.properties.* are read from ~/helidon/conf/examples.yaml. +# oci: vault: # Vault OCID (the vault you want to use for this example diff --git a/examples/security/vaults/pom.xml b/examples/security/vaults/pom.xml index 4319807d1fd..fe7e1e109f7 100644 --- a/examples/security/vaults/pom.xml +++ b/examples/security/vaults/pom.xml @@ -76,10 +76,6 @@ io.helidon.integrations.vault.auths helidon-integrations-vault-auths-token - - io.helidon.integrations.oci - helidon-integrations-oci-vault - org.junit.jupiter junit-jupiter-api diff --git a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java index e5fc759c2a8..298f2dd60d7 100644 --- a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java +++ b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java @@ -212,6 +212,10 @@ private static PoolDataSource createDataSource(final Instance instance, } final Object serviceName = connectionFactoryProperties.remove("serviceName"); final Object pdbRoles = connectionFactoryProperties.remove("pdbRoles"); + // Used for OCI ATP Integration + // Removing this so that it is not set on connectionFactoryProperties, + // Else we get exception with getConnection using this DS, if its set. + connectionFactoryProperties.remove("tnsNetServiceName"); if (!connectionFactoryProperties.stringPropertyNames().isEmpty()) { // We found some String-typed properties that are destined for the underlying connection factory to // hopefully fully configure it. Apply them here. diff --git a/integrations/cdi/oci-objectstorage-cdi/README.md b/integrations/cdi/oci-objectstorage-cdi/README.md deleted file mode 100644 index 7660bb904a6..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Helidon OCI Object Storage CDI Integration - -The Helidon OCI Object Storage CDI Integration project supplies a -[CDI portable extension](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#spi) - that lets the end user inject an `ObjectStorage` client into her CDI application. - -## Installation - -Ensure that the Helidon OCI Object Storage CDI Integration project and -its runtime dependencies are present on your application's runtime -classpath. - -For Maven users, your `` stanza should look like this: - -```xml - - io.helidon.integrations.cdi - helidon-integrations-cdi-oci-objectstorage - 1.0.0 - runtime - -``` - -## Usage - -If you want to use an `ObjectStorage` client -in your application code, simply inject it in the -[usual, idiomatic CDI way](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#injection_and_resolution). - Here is a field injection example: - -```java -@Inject -private ObjectStorage client; -``` - -And here is a constructor injection example: - -```java -private final ObjectStorage client; - -@Inject -public YourConstructor(@Named("orders") ObjectStorage client) { - super(); - this.client = client; -} -``` - -The Helidon OCI Object Storage CDI Integration project will satisfy -this injection point with an `ObjectStorageClient` in -[application scope](http://docs.jboss.org/cdi/api/2.0/javax/enterprise/context/ApplicationScoped.html). - -To create it, the Helidon OCI Object Storage CDI Integration project -will use [MicroProfile Config](https://static.javadoc.io/org.eclipse.microprofile.config/microprofile-config-api/1.3/index.html?overview-summary.html) - to locate its configuration. The following - [Property names](https://static.javadoc.io/org.eclipse.microprofile.config/microprofile-config-api/1.3/org/eclipse/microprofile/config/Config.html#getPropertyNames--) - will be used to establish a connection to the OCI Object Storage service: - -* `oci.auth.fingerprint` -* `oci.auth.keyFile` -* `oci.auth.passphraseCharacters` -* `oci.auth.user` -* `oci.auth.tenancy` -* `oci.objectstorage.region` - -These properties are [documented in the OCI Object Storage Java SDK documentation](https://docs.cloud.oracle.com/iaas/Content/API/SDKDocs/javasdk.htm#Configur). diff --git a/integrations/cdi/oci-objectstorage-cdi/etc/spotbugs/exclude.xml b/integrations/cdi/oci-objectstorage-cdi/etc/spotbugs/exclude.xml deleted file mode 100644 index 4b53e801a5e..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/etc/spotbugs/exclude.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/integrations/cdi/oci-objectstorage-cdi/pom.xml b/integrations/cdi/oci-objectstorage-cdi/pom.xml deleted file mode 100644 index a584f6166bf..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/pom.xml +++ /dev/null @@ -1,117 +0,0 @@ - - - - 4.0.0 - - io.helidon.integrations.cdi - helidon-integrations-cdi-project - 3.0.0-SNAPSHOT - - helidon-integrations-cdi-oci-objectstorage - Helidon CDI Integrations OCI Object Storage - - - - package - - - - etc/spotbugs/exclude.xml - - - - - - maven-surefire-plugin - - - ${oci.objectstorage.compartmentId} - ${oci.objectstorage.namespaceName} - ${oci.objectstorage.region} - - - - - - - - - - com.oracle.oci.sdk - oci-java-sdk-objectstorage - compile - pom - - - org.eclipse.microprofile.config - microprofile-config-api - compile - - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - - - org.jboss - jandex - runtime - true - - - io.helidon.microprofile.config - helidon-microprofile-config - runtime - true - - - org.glassfish.jersey.inject - jersey-hk2 - runtime - - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.hamcrest - hamcrest-all - test - - - io.helidon.microprofile.cdi - helidon-microprofile-cdi - test - - - org.slf4j - slf4j-simple - test - - - diff --git a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/MicroProfileConfigAuthenticationDetailsProvider.java b/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/MicroProfileConfigAuthenticationDetailsProvider.java deleted file mode 100644 index 74a9db46905..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/MicroProfileConfigAuthenticationDetailsProvider.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ -package io.helidon.integrations.cdi.oci.objectstorage; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Objects; - -import com.oracle.bmc.auth.CustomerAuthenticationDetailsProvider; -import org.eclipse.microprofile.config.Config; - -@Deprecated(forRemoval = true, since = "2.43") -final class MicroProfileConfigAuthenticationDetailsProvider extends CustomerAuthenticationDetailsProvider { - - private final Config config; - - MicroProfileConfigAuthenticationDetailsProvider(final Config config) { - super(); - this.config = Objects.requireNonNull(config); - } - - /** - * This method is implementing an interface of a third party. Until - * that interface removes this method, we need to keep it in Helidon. - * - * @return pass phrase - */ - @Deprecated - @Override - public String getPassPhrase() { - final char[] passphraseCharacters = this.getPassphraseCharacters(); - final String returnValue; - if (passphraseCharacters == null) { - returnValue = null; - } else { - returnValue = String.valueOf(passphraseCharacters); - } - return returnValue; - } - - @Override - public char[] getPassphraseCharacters() { - final String passphraseString = this.config.getOptionalValue("oci.auth.passphraseCharacters", String.class).orElse(null); - final char[] returnValue; - if (passphraseString == null) { - returnValue = null; - } else { - returnValue = passphraseString.toCharArray(); - } - return returnValue; - } - - @Override - public InputStream getPrivateKey() { - final String privateKey = this.config.getOptionalValue("oci.auth.privateKey", String.class) - .orElse(null); - if (privateKey == null || privateKey.trim().isEmpty()) { - final String pemFormattedPrivateKeyFilePath = - this.config.getOptionalValue("oci.auth.keyFile", String.class) - .orElse(Paths.get(System.getProperty("user.home"), ".oci/oci_api_key.pem").toString()); - assert pemFormattedPrivateKeyFilePath != null; - try { - return new BufferedInputStream(Files.newInputStream(Paths.get(pemFormattedPrivateKeyFilePath))); - } catch (final IOException ioException) { - throw new RuntimeException(ioException.getMessage(), ioException); - } - } else { - return new BufferedInputStream(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8))); - } - } - - @Override - public String getUserId() { - return this.config.getValue("oci.auth.user", String.class); - } - - @Override - public String getTenantId() { - return this.config.getValue("oci.auth.tenancy", String.class); - } - - @Override - public String getFingerprint() { - return this.config.getValue("oci.auth.fingerprint", String.class); - } - -} diff --git a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OCIObjectStorageExtension.java b/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OCIObjectStorageExtension.java deleted file mode 100644 index d46e5f1fa5d..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OCIObjectStorageExtension.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ -package io.helidon.integrations.cdi.oci.objectstorage; - -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.oracle.bmc.auth.AuthenticationDetailsProvider; -import com.oracle.bmc.objectstorage.ObjectStorage; -import com.oracle.bmc.objectstorage.ObjectStorageClient; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.AmbiguousResolutionException; -import jakarta.enterprise.inject.spi.AfterBeanDiscovery; -import jakarta.enterprise.inject.spi.Bean; -import jakarta.enterprise.inject.spi.BeanManager; -import jakarta.enterprise.inject.spi.Extension; -import jakarta.enterprise.inject.spi.InjectionPoint; -import jakarta.enterprise.inject.spi.ProcessInjectionPoint; -import jakarta.enterprise.inject.spi.ProcessManagedBean; -import jakarta.enterprise.inject.spi.ProcessProducerField; -import jakarta.enterprise.inject.spi.ProcessProducerMethod; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; - -/** - * An {@link Extension} that integrates the {@link ObjectStorage} - * interface into CDI-based applications. - * - * @see com.oracle.bmc.objectstorage.ObjectStorage - * - * @deprecated Please see the {@code - * io.helidon.integrations.oci.sdk.cdi.OciExtension} class instead. - */ -@Deprecated(forRemoval = true, since = "2.43") -public class OCIObjectStorageExtension implements Extension { - - private final Map, Object> objectStorageBeans; - - private final Map, Object> objectStorageClientBuilderBeans; - - private final Map, Object> authenticationDetailsProviderBeans; - - /** - * Creates a new {@link OCIObjectStorageExtension}. - */ - public OCIObjectStorageExtension() { - super(); - this.objectStorageBeans = new HashMap<>(7); - this.objectStorageClientBuilderBeans = new HashMap<>(7); - this.authenticationDetailsProviderBeans = new HashMap<>(7); - } - - private void processObjectStorageInjectionPoints(@Observes final ProcessInjectionPoint event) { - if (event != null) { - final InjectionPoint injectionPoint = event.getInjectionPoint(); - if (injectionPoint != null) { - this.objectStorageBeans.put(injectionPoint.getQualifiers(), null); - } - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processObjectStorageClientBuilderInjectionPoints(@Observes final ProcessInjectionPoint event) { - if (event != null) { - final InjectionPoint injectionPoint = event.getInjectionPoint(); - if (injectionPoint != null) { - this.objectStorageClientBuilderBeans.put(injectionPoint.getQualifiers(), null); - } - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processAuthenticationDetailsProviderInjectionPoints(@Observes final ProcessInjectionPoint event) { - if (event != null) { - final InjectionPoint injectionPoint = event.getInjectionPoint(); - if (injectionPoint != null) { - this.authenticationDetailsProviderBeans.put(injectionPoint.getQualifiers(), null); - } - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingAuthenticationDetailsProviderManagedBean(@Observes final ProcessManagedBean event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.authenticationDetailsProviderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.authenticationDetailsProviderBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingAuthenticationDetailsProviderProducerField(@Observes final ProcessProducerField event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.authenticationDetailsProviderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.authenticationDetailsProviderBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingAuthenticationDetailsProviderProducerMethod(@Observes final ProcessProducerMethod event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.authenticationDetailsProviderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.authenticationDetailsProviderBeans.put(qualifiers, bean); - } - } - - - /* - * ObjectStorageClient.Builder beans. - */ - - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingObjectStorageClientBuilderManagedBean(@Observes final ProcessManagedBean event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageClientBuilderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageClientBuilderBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingObjectStorageClientBuilderProducerField(@Observes final ProcessProducerField event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageClientBuilderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageClientBuilderBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingObjectStorageClientBuilderProducerMethod(@Observes final ProcessProducerMethod event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageClientBuilderBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageClientBuilderBeans.put(qualifiers, bean); - } - } - - - /* - * ObjectStorage beans. - */ - - - private void processPreexistingObjectStorageManagedBean(@Observes final ProcessManagedBean event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingObjectStorageProducerField(@Observes final ProcessProducerField event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageBeans.put(qualifiers, bean); - } - } - - @SuppressWarnings("checkstyle:linelength") - private void processPreexistingObjectStorageProducerMethod(@Observes final ProcessProducerMethod event) { - if (event != null) { - final Bean bean = event.getBean(); - assert bean != null; - final Set qualifiers = bean.getQualifiers(); - if (this.objectStorageBeans.containsKey(bean.getQualifiers())) { - throw new AmbiguousResolutionException(); - } - this.objectStorageBeans.put(qualifiers, bean); - } - } - - - /* - * Generated beans. - */ - - - private void addBeans(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) { - if (event != null && beanManager != null) { - - // We can't look up Config as a CDI bean here because it is - // illegal to call beanManager.getReference() until - // AfterDeploymentValidation time, by which point it is too late - // to add custom beans. And we don't want to look it up - // expensively inside each bean's create() method. So we do it - // "manually" here. - final Config config = ConfigProvider.getConfig(); - assert config != null; - - if (!this.authenticationDetailsProviderBeans.isEmpty()) { - for (final Entry, ?> adpEntry : this.authenticationDetailsProviderBeans.entrySet()) { - assert adpEntry != null; - if (adpEntry.getValue() == null) { - // There was a qualified or default injection point, but - // no bean to satisfy it. Generate one. - final Set qualifiers = adpEntry.getKey(); - assert qualifiers != null; - assert !qualifiers.isEmpty(); - event.addBean() - .scope(ApplicationScoped.class) - .addTransitiveTypeClosure(MicroProfileConfigAuthenticationDetailsProvider.class) - .beanClass(MicroProfileConfigAuthenticationDetailsProvider.class) - .qualifiers(qualifiers) - .createWith(cc -> new MicroProfileConfigAuthenticationDetailsProvider(config)); - } - } - } - - if (!this.objectStorageClientBuilderBeans.isEmpty()) { - for (final Entry, ?> oscbEntry : this.objectStorageClientBuilderBeans.entrySet()) { - assert oscbEntry != null; - if (oscbEntry.getValue() == null) { - // There was a qualified or default injection point, but - // no bean to satisfy it. Generate one. - final Set qualifiers = oscbEntry.getKey(); - assert qualifiers != null; - assert !qualifiers.isEmpty(); - final Annotation[] qualifiersArray = qualifiers.toArray(new Annotation[qualifiers.size()]); - event.addBean() - .scope(ApplicationScoped.class) - .addTransitiveTypeClosure(ObjectStorageClient.Builder.class) - .beanClass(ObjectStorageClient.Builder.class) - .qualifiers(qualifiers) - .createWith(cc -> { - final ObjectStorageClient.Builder builder = ObjectStorageClient.builder(); - assert builder != null; - // Permit further customization before the bean is actually created - beanManager.getEvent().select(ObjectStorageClient.Builder.class, qualifiersArray).fire(builder); - return builder; - }); - } - } - } - - if (!this.objectStorageBeans.isEmpty()) { - for (final Entry, ?> osbEntry : this.objectStorageBeans.entrySet()) { - assert osbEntry != null; - if (osbEntry.getValue() == null) { - // There was a qualified or default injection point, but - // no bean to satisfy it. Generate one. - final Set qualifiers = osbEntry.getKey(); - assert qualifiers != null; - assert !qualifiers.isEmpty(); - final Annotation[] qualifiersArray = qualifiers.toArray(new Annotation[qualifiers.size()]); - event.addBean() - .scope(ApplicationScoped.class) - .addTransitiveTypeClosure(ObjectStorageClient.class) - .beanClass(ObjectStorageClient.class) - .qualifiers(qualifiers) - .createWith(cc -> { - Set> beans = beanManager.getBeans(ObjectStorageClient.Builder.class, qualifiersArray); - final ObjectStorageClient.Builder builder; - if (beans == null || beans.isEmpty()) { - builder = ObjectStorageClient.builder(); - assert builder != null; - // Permit further customization before the bean is actually created - beanManager.getEvent().select(ObjectStorageClient.Builder.class, qualifiersArray).fire(builder); - } else { - final Bean bean = beanManager.resolve(beans); - assert bean != null; - builder = (ObjectStorageClient.Builder) beanManager.getReference(bean, - ObjectStorageClient.Builder.class, - beanManager.createCreationalContext(bean)); - } - assert builder != null; - - beans = beanManager.getBeans(AuthenticationDetailsProvider.class, qualifiersArray); - final AuthenticationDetailsProvider authProvider; - if (beans == null || beans.isEmpty()) { - authProvider = new MicroProfileConfigAuthenticationDetailsProvider(config); - } else { - final Bean bean = beanManager.resolve(beans); - assert bean != null; - authProvider = - (AuthenticationDetailsProvider) beanManager.getReference(bean, - AuthenticationDetailsProvider.class, - beanManager.createCreationalContext(bean)); - } - assert authProvider != null; - final ObjectStorage objectStorage = builder.build(authProvider); - assert objectStorage != null; - objectStorage.setRegion(config.getValue("oci.objectstorage.region", String.class)); // hack - return objectStorage; - }); - } - } - } - - } - this.authenticationDetailsProviderBeans.clear(); - this.objectStorageBeans.clear(); - this.objectStorageClientBuilderBeans.clear(); - } - -} diff --git a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OciConfigConfigSource.java b/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OciConfigConfigSource.java deleted file mode 100644 index a2ad394c5f7..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/OciConfigConfigSource.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ -package io.helidon.integrations.cdi.oci.objectstorage; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.spi.ConfigProviderResolver; -import org.eclipse.microprofile.config.spi.ConfigSource; - -/** - * A {@link ConfigSource} implementation that is backed by a {@link - * ConfigFileAuthenticationDetailsProvider}. - * - * @deprecated Please see the {@code - * io.helidon.integrations.oci.sdk.cdi.OciExtension} class for more - * details. - */ -@Deprecated(forRemoval = true, since = "2.43") -public final class OciConfigConfigSource implements ConfigSource { - - private volatile Map properties; - - /** - * Creates a new {@link OciConfigConfigSource}. - */ - public OciConfigConfigSource() { - super(); - } - - /** - * Returns the name of this {@link OciConfigConfigSource}. - * - *

This method never returns {@code null}.

- * - *

This method returns the same value every time it is - * invoked.

- * - *

Overrides of this method must not return {@code null}.

- * - *

Overrides of this method must return the same value every time - * they are invoked.

- * - *

The default return value of this method is subject to change - * without notice.

- * - * @return the name of this {@link OciConfigConfigSource}; never - * {@code null} - * - * @see ConfigSource#getName() - */ - @Override - public String getName() { - return ".oci/config"; - } - - /** - * Returns the ordinal of this {@link OciConfigConfigSource}. - * - *

This implementation returns {@code 101}, which will ensure - * values from this {@link ConfigSource} implementation will trump - * those from {@code /META-INF/microprofile-config.properties} but - * none other.

- * - * @return the ordinal of this {@link OciConfigConfigSource}; {@code - * 101} by default - */ - @Override - public int getOrdinal() { - return 101; // one higher than microprofile-config.properties' ordinal - } - - /** - * Returns a value for the supplied {@code propertyName}, or {@code - * null} if there is no such value. - * - *

This method may return {@code null}.

- * - * @param propertyName the name of the property for which a value - * should be returned; may be {@code null} in which case {@code - * null} will be returned - * - * @return a value for the supplied {@code propertyName}, or {@code - * null} - */ - @Override - public String getValue(final String propertyName) { - final String returnValue; - if (propertyName == null) { - returnValue = null; - } else if (propertyName.equals(ConfigSource.CONFIG_ORDINAL)) { - returnValue = String.valueOf(this.getOrdinal()); - } else { - Map properties = this.properties; - if (properties == null) { - final Config config = ConfigProviderResolver.instance() - .getBuilder() - .addDefaultSources() - .build(); - final String profile = config.getOptionalValue("oci.auth.profile", String.class).orElse("DEFAULT"); - final String configFilePath = config.getOptionalValue("oci.config.path", String.class).orElse(null); - final ConfigFileAuthenticationDetailsProvider provider; - ConfigFileAuthenticationDetailsProvider temp = null; - try { - if (configFilePath == null) { - temp = new ConfigFileAuthenticationDetailsProvider(profile); - } else { - temp = new ConfigFileAuthenticationDetailsProvider(configFilePath, profile); - } - } catch (final IOException ioException) { - temp = null; - } finally { - provider = temp; - } - properties = createProperties(provider); - this.properties = properties; - } - returnValue = properties.get(propertyName); - } - return returnValue; - } - - /** - * Returns a {@link Map} consisting of all property names and their - * values that this {@link OciConfigConfigSource} knows about at the - * time that this method is invoked. - * - *

This method never returns {@code null}.

- * - *

The returned {@link Map} is {@linkplain - * Collections#unmodifiableMap(Map) immutable} and safe for - * concurrent use by multiple threads.

- * - *

This method may return different {@link Map} instances when - * invoked at different times.

- * - *

The returned {@link Map}, if non-{@linkplain Map#isEmpty() - * empty}, is guaranteed to contain at least the following keys:

- * - *
    - * - *
  • oci.auth.fingerprint
  • - * - *
  • oci.auth.passphraseCharacters
  • - * - *
  • oci.auth.tenancy
  • - * - *
  • oci.auth.user
  • - * - *
- * - *

The MicroProfile Config specification does not give any - * guidance on whether the return value of an implementation of the - * {@link ConfigSource#getProperties()} method should be immutable - * and/or threadsafe. This implementation returns an {@linkplain - * Collections#unmodifiableMap(Map) immutable Map}.

- * - * @return a non-{@code null} {@link Map} of properties known to - * this {@link OciConfigConfigSource} - * - * @exception IllegalStateException if there was a problem reading - * the OCI config file - * - * @see ConfigSource#getProperties() - */ - @Override - public Map getProperties() { - final Map properties = this.properties; - return properties == null || properties.isEmpty() ? Collections.emptyMap() : properties; - } - - @Override - public Set getPropertyNames() { - return Set.copyOf(getProperties().keySet()); - } - - private static Map createProperties(final ConfigFileAuthenticationDetailsProvider provider) { - final Map returnValue; - if (provider == null) { - returnValue = Collections.emptyMap(); - } else { - final Map properties = new HashMap<>(); - properties.put("oci.auth.fingerprint", provider.getFingerprint()); - char[] passphrase = provider.getPassphraseCharacters(); - if (passphrase != null && passphrase.length > 0) { - properties.put("oci.auth.passphraseCharacters", String.valueOf(passphrase)); - } - properties.put("oci.auth.tenancy", provider.getTenantId()); - properties.put("oci.auth.user", provider.getUserId()); - returnValue = Collections.unmodifiableMap(properties); - } - return returnValue; - } - -} diff --git a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/package-info.java b/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/package-info.java deleted file mode 100644 index 20f3455ecd2..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/main/java/io/helidon/integrations/cdi/oci/objectstorage/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -/** - * Provides classes and interfaces that integrate the OCI object - * storage service into CDI 3.0-based applications. - * - * @deprecated Please use classes from the {@code - * io.helidon.integrations.sdk.cdi} package instead. - */ -package io.helidon.integrations.cdi.oci.objectstorage; diff --git a/integrations/cdi/oci-objectstorage-cdi/src/main/java/module-info.java b/integrations/cdi/oci-objectstorage-cdi/src/main/java/module-info.java deleted file mode 100644 index 784a5f79626..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/main/java/module-info.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -/** - * Provides classes and interfaces that integrate the OCI object - * storage service into CDI 2.0-based applications. - */ -module io.helidon.integrations.cdi.oci.objectstorage { - - requires jakarta.inject; - requires oci.java.sdk.common; - requires oci.java.sdk.objectstorage.generated; - requires oci.java.sdk.objectstorage.extensions; - requires jakarta.cdi; - requires microprofile.config.api; - - exports io.helidon.integrations.cdi.oci.objectstorage; - - provides jakarta.enterprise.inject.spi.Extension - with io.helidon.integrations.cdi.oci.objectstorage.OCIObjectStorageExtension; - - provides org.eclipse.microprofile.config.spi.ConfigSource - with io.helidon.integrations.cdi.oci.objectstorage.OciConfigConfigSource; -} diff --git a/integrations/cdi/oci-objectstorage-cdi/src/test/java/io/helidon/integrations/cdi/oci/objectstorage/TestOCIObjectStorageExtension.java b/integrations/cdi/oci-objectstorage-cdi/src/test/java/io/helidon/integrations/cdi/oci/objectstorage/TestOCIObjectStorageExtension.java deleted file mode 100644 index d0f5a38e057..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/test/java/io/helidon/integrations/cdi/oci/objectstorage/TestOCIObjectStorageExtension.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ -package io.helidon.integrations.cdi.oci.objectstorage; - -import com.oracle.bmc.objectstorage.ObjectStorage; -import com.oracle.bmc.objectstorage.ObjectStorageClient; -import com.oracle.bmc.objectstorage.requests.ListBucketsRequest; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.context.Initialized; -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.se.SeContainer; -import jakarta.enterprise.inject.se.SeContainerInitializer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assumptions.assumeFalse; - -@ApplicationScoped -public class TestOCIObjectStorageExtension { - - private SeContainer cdiContainer; - - TestOCIObjectStorageExtension() { - super(); - } - - @BeforeEach - void startCdiContainer() { - assumeFalse(System.getProperty("oci.objectstorage.compartmentId", "").isEmpty()); - assumeFalse(System.getProperty("oci.objectstorage.namespaceName", "").isEmpty()); - assumeFalse(System.getProperty("oci.objectstorage.region", "").isEmpty()); - final SeContainerInitializer initializer = SeContainerInitializer.newInstance(); - assertNotNull(initializer); - this.cdiContainer = initializer.initialize(); - } - - @AfterEach - void shutDownCdiContainer() { - if (this.cdiContainer != null) { - this.cdiContainer.close(); - } - } - - private void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event, - final ObjectStorage client) { - assertNotNull(client); - assertNotNull(client.toString()); // dereference the proxy - final ListBucketsRequest request = - ListBucketsRequest.builder() - .compartmentId(System.getProperty("oci.objectstorage.compartmentId")) - .namespaceName(System.getProperty("oci.objectstorage.namespaceName")) - .build(); - client.listBuckets(request); - } - - private void configure(@Observes final ObjectStorageClient.Builder builder) { - assertNotNull(builder); - } - - @Test - void testSpike() { - - } - -} diff --git a/integrations/cdi/oci-objectstorage-cdi/src/test/resources/META-INF/beans.xml b/integrations/cdi/oci-objectstorage-cdi/src/test/resources/META-INF/beans.xml deleted file mode 100644 index 1b3fbc297cb..00000000000 --- a/integrations/cdi/oci-objectstorage-cdi/src/test/resources/META-INF/beans.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/integrations/cdi/pom.xml b/integrations/cdi/pom.xml index 3ef57159891..f72775d29f3 100644 --- a/integrations/cdi/pom.xml +++ b/integrations/cdi/pom.xml @@ -42,6 +42,5 @@ jpa-cdi jta-cdi jta-weld - oci-objectstorage-cdi diff --git a/integrations/oci/atp/pom.xml b/integrations/oci/atp/pom.xml deleted file mode 100644 index 5117064e475..00000000000 --- a/integrations/oci/atp/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-atp - Helidon Integrations OCI ATP - - OCI ATP Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - - - io.helidon.common - helidon-common - - - io.helidon.config - helidon-config - - - com.oracle.database.jdbc - ojdbc8-production - pom - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - true - - - - org.eclipse.microprofile.config - microprofile-config-api - provided - true - - - \ No newline at end of file diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/GenerateAutonomousDatabaseWallet.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/GenerateAutonomousDatabaseWallet.java deleted file mode 100644 index 56ad6e0f1f4..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/GenerateAutonomousDatabaseWallet.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.security.KeyStore; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; - -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import oracle.security.pki.OraclePKIProvider; - -/** - * GenerateAutonomousDatabaseWallet request and response. - */ -public final class GenerateAutonomousDatabaseWallet { - private GenerateAutonomousDatabaseWallet() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Set explicit password to encrypt the keys inside the wallet. - * - * @param walletPassword walletPassword - * @return updated request - */ - public Request password(String walletPassword) { - return add("password", walletPassword); - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final WalletArchive walletArchive; - - private Response(WalletArchive walletArchive) { - this.walletArchive = walletArchive; - } - - static Response create(byte[] bytes) { - return new Response(new WalletArchive(bytes)); - } - - /** - * Returns the wallet retrieved. - * - * @return WalletArchive - wallet data - */ - public WalletArchive walletArchive() { - return walletArchive; - } - - } - - /** - * Object to store wallet returned for ATP as bytes[]. - */ - public static class WalletArchive { - private final byte[] content; - - /** - * Set wallet data. - * - * @param content - */ - public WalletArchive(byte[] content) { - this.content = content.clone(); - } - - /** - * Returns wallet data. - * - * @return bytes[] - */ - public byte[] getContent() { - return content.clone(); - } - - /** - * Returns SSLContext based on cwallet.sso in wallet. - * - * @return SSLContext - */ - public SSLContext getSSLContext() throws IllegalStateException { - SSLContext sslContext = null; - try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(content))) { - ZipEntry entry = null; - while ((entry = zis.getNextEntry()) != null) { - if (entry.getName().equals("cwallet.sso")) { - KeyStore keyStore = KeyStore.getInstance("SSO", new OraclePKIProvider()); - keyStore.load(zis, null); - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX"); - trustManagerFactory.init(keyStore); - keyManagerFactory.init(keyStore, null); - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); - } - zis.closeEntry(); - } - } catch (Exception e) { - throw new IllegalStateException("Error while getting SSLContext from wallet.", e); - } - return sslContext; - } - - /** - * Returns JDBC URL with connection description for the given service based on tnsnames.ora in wallet. - * - * @param serviceName - * @return String - */ - public String getJdbcUrl(String serviceName) throws IllegalStateException { - String jdbcUrl = null; - try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(content))) { - ZipEntry entry = null; - while ((entry = zis.getNextEntry()) != null) { - if (entry.getName().equals("tnsnames.ora")) { - jdbcUrl = new String(zis.readAllBytes(), StandardCharsets.UTF_8) - .replaceFirst(serviceName + "_high\\s+=\\s+", "jdbc:oracle:thin:@") - .replaceAll("\\n[^\\n]+", ""); - } - zis.closeEntry(); - } - } catch (Exception e) { - throw new IllegalStateException("Error while getting JDBC URL from wallet.", e); - } - return jdbcUrl; - } - } -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDb.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDb.java deleted file mode 100644 index 9595813348b..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDb.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -/** - * Blocking OCI ATP API. - * All methods block the current thread. This implementation is not suitable for reactive programming. - * Use {@link OciAutonomousDbRx} in reactive code. - */ -public interface OciAutonomousDb { - /** - * Create a blocking ATP integration from its reactive counterpart. - * When running within an injection capable environment (such as CDI), instances of this - * class can be injected. - * - * @param reactive reactive OCI ATP - * @return blocking OCI ATP - */ - static OciAutonomousDb create(OciAutonomousDbRx reactive) { - return new OciAutonomousDbImpl(reactive); - } - - /** - * Gets the metadata and body of Wallet. - * - * @param request get object request - * @return future with response or error - */ - ApiOptionalResponse generateWallet( - GenerateAutonomousDatabaseWallet.Request request); -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbImpl.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbImpl.java deleted file mode 100644 index b177aea192c..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -class OciAutonomousDbImpl implements OciAutonomousDb { - private final OciAutonomousDbRx delegate; - - OciAutonomousDbImpl(OciAutonomousDbRx delegate) { - this.delegate = delegate; - } - - @Override - public ApiOptionalResponse generateWallet( - GenerateAutonomousDatabaseWallet.Request request) { - return delegate.generateWallet(request).await(); - } -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbInjectionProvider.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbInjectionProvider.java deleted file mode 100644 index 5c4aafc5cd2..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbInjectionProvider.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import java.util.LinkedList; -import java.util.List; - -import io.helidon.integrations.oci.connect.spi.InjectionProvider; - -/** - * Service provider for {@link io.helidon.integrations.oci.connect.spi.InjectionProvider}. - * Only use by service loader. - * @deprecated do not use directly - */ -@Deprecated -public class OciAutonomousDbInjectionProvider implements InjectionProvider { - private static final List> INJECTABLES; - - static { - List> injectables = new LinkedList<>(); - - injectables.add(InjectionType.create(OciAutonomousDbRx.class, - (restApi, config) -> OciAutonomousDbRx.builder() - .restApi(restApi) - .config(config) - .build())); - - injectables.add(InjectionType.create(OciAutonomousDb.class, - (restApi, config) -> OciAutonomousDb.create(OciAutonomousDbRx.builder() - .restApi(restApi) - .config(config) - .build()))); - INJECTABLES = List.copyOf(injectables); - } - - /** - * This constructor is only intended for service loader. - * DO NOT USE DIRECTLY. - * @deprecated do not use - */ - @Deprecated - public OciAutonomousDbInjectionProvider() { - } - - @Override - public List> injectables() { - return INJECTABLES; - } -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRx.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRx.java deleted file mode 100644 index 45cc29a71b7..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRx.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import java.util.function.Consumer; - -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciRestApi; - -/** - * Reactive API for OCI ATP. - */ -public interface OciAutonomousDbRx { - /** - * Version of ATP API supported by this client. - */ - String API_VERSION = "20160918"; - - /** - * Host name prefix. - */ - String API_HOST_PREFIX = "database"; - - /** - * Host format of API server. - */ - String API_HOST_FORMAT = "%s://%s.%s.%s"; - - /** - * Create a new fluent API builder for OCI ATP. - * - * @return a new builder - */ - static Builder builder() { - return new Builder(); - } - - /** - * Create OCI ATP using the default {@link io.helidon.integrations.oci.connect.OciRestApi}. - * - * @return OCI ATP instance connecting based on {@code DEFAULT} profile - */ - static OciAutonomousDbRx create() { - return builder().build(); - } - - /** - * Create OCI ATP based on configuration. - * - * @param config configuration on the node of OCI configuration - * @return OCI ATP instance configured from the configuration - * @see OciAutonomousDbRx.Builder#config(io.helidon.config.Config) - */ - static OciAutonomousDbRx create(Config config) { - return builder().config(config).build(); - } - - /** - * Gets the metadata and body of Wallet. - * - * @param request get object request - * @return future with response or error - */ - Single> generateWallet( - GenerateAutonomousDatabaseWallet.Request request); - - /** - * Fluent API Builder for {@link OciAutonomousDbRx}. - */ - class Builder implements io.helidon.common.Builder { - private final OciRestApi.Builder apiBuilder = OciRestApi.builder(); - - private String hostPrefix = API_HOST_PREFIX; - private String endpoint; - private String ocid; - private String walletPassword; - private OciRestApi restApi; - - private Builder() { - } - - @Override - public OciAutonomousDbRx build() { - if (restApi == null) { - restApi = apiBuilder.build(); - } - return new OciAutonomousDbRxImpl(this); - } - - /** - * Update from configuration. The configuration must be located on the {@code OCI} root configuration - * node. - * - * @param config configuration - * @return updated builder - */ - public Builder config(Config config) { - apiBuilder.config(config); - config.get("atp.host-prefix").asString().ifPresent(this::hostPrefix); - config.get("atp.endpoint").asString().ifPresent(this::endpoint); - config.get("atp.ocid").asString().ifPresent(this::ocid); - config.get("atp.walletPassword").asString().ifPresent(this::walletPassword); - return this; - } - - /** - * Instance of rest API to use. - * - * @param restApi rest API - * @return updated builder - */ - public Builder restApi(OciRestApi restApi) { - this.restApi = restApi; - return this; - } - - /** - * Host prefix to use for object storage, - * defaults to {@value API_HOST_PREFIX}. - * - * @param prefix prefix to use - * @return updated builder - */ - public Builder hostPrefix(String prefix) { - this.hostPrefix = prefix; - return this; - } - - /** - * Explicit endpoint to use. - * - * @param endpoint endpoint - * @return updated builder - */ - public Builder endpoint(String endpoint) { - this.endpoint = endpoint; - return this; - } - - /** - * Explicit ocid of ATP to use. - * - * @param ocid ocid - * @return updated builder - */ - public Builder ocid(String ocid) { - this.ocid = ocid; - return this; - } - - /** - * Set explicit password to encrypt the keys inside the wallet. - * - * @param walletPassword walletPassword - * @return updated builder - */ - public Builder walletPassword(String walletPassword) { - this.walletPassword = walletPassword; - return this; - } - - /** - * Update the rest access builder to modify defaults. - * - * @param builderConsumer consumer of the builder - * @return updated builder - */ - public Builder updateRestApi(Consumer builderConsumer) { - builderConsumer.accept(apiBuilder); - return this; - } - - OciRestApi restApi() { - return restApi; - } - - String hostPrefix() { - return hostPrefix; - } - - String endpoint() { - return endpoint; - } - - String ocid() { - return ocid; - } - - String walletPassword() { - return walletPassword; - } - - } -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRxImpl.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRxImpl.java deleted file mode 100644 index 54b3c314fc9..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/OciAutonomousDbRxImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.atp; - -import java.util.Optional; - -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Single; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciRestApi; - -class OciAutonomousDbRxImpl implements OciAutonomousDbRx { - private final OciRestApi restApi; - private final String hostPrefix; - private final Optional endpoint; - private final String ocid; - private final String walletPassword; - - OciAutonomousDbRxImpl(Builder builder) { - this.restApi = builder.restApi(); - this.hostPrefix = builder.hostPrefix(); - this.endpoint = Optional.ofNullable(builder.endpoint()); - this.ocid = builder.ocid(); - this.walletPassword = builder.walletPassword(); - } - - @Override - public Single> generateWallet( - GenerateAutonomousDatabaseWallet.Request request) { - String apiPath = "/20160918/autonomousDatabases/" + this.ocid + "/actions/generateWallet"; - - if (!request.endpoint().isPresent()) { - endpoint.ifPresent(request::endpoint); - request.hostFormat(OciAutonomousDbRx.API_HOST_FORMAT) - .hostPrefix(hostPrefix); - } - - request.password(this.walletPassword); - - return restApi.invokeBytesResponse(Http.Method.POST, - apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(GenerateAutonomousDatabaseWallet.Response::create)); - } -} diff --git a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/package-info.java b/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/package-info.java deleted file mode 100644 index e62f248ba86..00000000000 --- a/integrations/oci/atp/src/main/java/io/helidon/integrations/oci/atp/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ - -/** - * OCI ATP integration. - * - * @see io.helidon.integrations.oci.atp.OciAutonomousDb - * @see io.helidon.integrations.oci.atp.OciAutonomousDbRx - */ -package io.helidon.integrations.oci.atp; diff --git a/integrations/oci/atp/src/main/java/module-info.java b/integrations/oci/atp/src/main/java/module-info.java deleted file mode 100644 index 1e52667d91c..00000000000 --- a/integrations/oci/atp/src/main/java/module-info.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - */ - -import io.helidon.integrations.oci.atp.OciAutonomousDb; -import io.helidon.integrations.oci.atp.OciAutonomousDbInjectionProvider; -import io.helidon.integrations.oci.atp.OciAutonomousDbRx; - -/** - * OCI ATP integration. - * - * @see OciAutonomousDb - * @see OciAutonomousDbRx - */ -module io.helidon.integrations.oci.atp { - requires transitive jakarta.json; - requires transitive io.helidon.common.reactive; - requires transitive io.helidon.integrations.oci.connect; - requires transitive io.helidon.config; - - requires io.helidon.integrations.common.rest; - requires io.helidon.common.http; - - requires oraclepki; - - exports io.helidon.integrations.oci.atp; - - // this is the intended usage, deprecation is to warn about accidental usage in code - //noinspection deprecation - provides io.helidon.integrations.oci.connect.spi.InjectionProvider - with OciAutonomousDbInjectionProvider; - - opens io.helidon.integrations.oci.atp to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/atp/src/main/resources/META-INF/helidon/native-image/weld-proxies.json b/integrations/oci/atp/src/main/resources/META-INF/helidon/native-image/weld-proxies.json deleted file mode 100644 index fb7051fb0f2..00000000000 --- a/integrations/oci/atp/src/main/resources/META-INF/helidon/native-image/weld-proxies.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.atp.OciAutonomousDb" - ] - }, - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.atp.OciAutonomousDbRx" - ] - } -] \ No newline at end of file diff --git a/integrations/oci/cdi/pom.xml b/integrations/oci/cdi/pom.xml deleted file mode 100644 index 8a1dd2ed548..00000000000 --- a/integrations/oci/cdi/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-cdi - Helidon Integrations OCI CDI - - OCI CDI integration - - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - - - jakarta.enterprise - jakarta.enterprise.cdi-api - - - io.helidon.microprofile.cdi - helidon-microprofile-cdi - - - \ No newline at end of file diff --git a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciCdiExtension.java b/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciCdiExtension.java deleted file mode 100644 index 33d141bd4eb..00000000000 --- a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciCdiExtension.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.cdi; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.HashSet; -import java.util.List; -import java.util.ServiceLoader; -import java.util.Set; - -import io.helidon.common.serviceloader.HelidonServiceLoader; -import io.helidon.config.Config; -import io.helidon.integrations.oci.connect.OciRestApi; -import io.helidon.integrations.oci.connect.spi.InjectionProvider; -import io.helidon.microprofile.cdi.RuntimeStart; - -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.literal.NamedLiteral; -import jakarta.enterprise.inject.spi.AfterBeanDiscovery; -import jakarta.enterprise.inject.spi.Annotated; -import jakarta.enterprise.inject.spi.CDI; -import jakarta.enterprise.inject.spi.Extension; -import jakarta.enterprise.inject.spi.InjectionPoint; -import jakarta.enterprise.inject.spi.ProcessBean; -import jakarta.enterprise.inject.spi.ProcessInjectionPoint; -import jakarta.inject.Named; - -/** - * CDI extension that adds injection support for OCI related APIs. - * This extension adds all injectables discovered through {@link io.helidon.integrations.oci.connect.spi.InjectionProvider} - * SPI. - * This extension also adds support for injection of {@code OciRestApi}, with named instances - * obtained from configuration. - */ -public class OciCdiExtension implements Extension { - private final Set supportedTypes = new HashSet<>(); - private final Set requiredNames = new HashSet<>(); - private final List providers; - - private Config config; - - /** - * Default constructor, used by CDI when discovering extensions through service loader, - * or directly when configuring an extension instance with CDI. - */ - public OciCdiExtension() { - providers = HelidonServiceLoader - .builder(ServiceLoader.load(InjectionProvider.class)) - .build() - .asList(); - - for (InjectionProvider provider : providers) { - provider.injectables() - .stream() - .map(InjectionProvider.InjectionType::injectedType) - .forEach(supportedTypes::add); - } - } - - private void configure(@Observes @RuntimeStart Config config) { - this.config = config.get("oci"); - } - - /** - * Add internal qualifier. - * - * @param event CDI event - */ - void updateInjectionPoints(@Observes ProcessInjectionPoint event) { - InjectionPoint injectionPoint = event.getInjectionPoint(); - Annotated annotated = injectionPoint.getAnnotated(); - - Type type = injectionPoint.getType(); - if (supportedTypes.contains(type)) { - Named name = annotated.getAnnotation(Named.class); - - OciInternal internal = OciInternal.Literal - .create((name == null ? "" : name.value())); - - event.configureInjectionPoint() - .addQualifier(internal); - } - } - - /** - * Collect injection points that are valid. - * - * @param event CDI event - */ - void processInjectionPointsFromEnabledBeans(@Observes ProcessBean event) { - for (InjectionPoint injectionPoint : event.getBean().getInjectionPoints()) { - Set qualifiers = injectionPoint.getQualifiers(); - for (Annotation qualifier : qualifiers) { - if (qualifier.annotationType().equals(OciInternal.class)) { - OciInternal value = (OciInternal) qualifier; - - requiredNames.add(value.value()); - break; - } - } - } - } - - void registerProducers(@Observes AfterBeanDiscovery abd) { - if (requiredNames.contains("")) { - requiredNames.add("default"); - } else if (requiredNames.contains("default")) { - requiredNames.add(""); - } - boolean addDefault = true; - - for (String name : requiredNames) { - Named named = NamedLiteral.of(name); - - if (name.isEmpty() || name.equals("default")) { - if (addDefault) { - // default - abd.addBean(new QualifiedBean<>(OciCdiExtension.class, - OciRestApi.class, - () -> { - if (config.get("default").exists()) { - return OciRestApi.create(config.get("default")); - } else { - return OciRestApi.create(config); - } - })); - - abd.addBean(new QualifiedBean<>(OciCdiExtension.class, - OciRestApi.class, - Set.of(NamedLiteral.of(""), NamedLiteral.of("default")), - () -> { - if (config.get("default").exists()) { - return OciRestApi.create(config.get("default")); - } else { - return OciRestApi.create(config); - } - })); - addDefault = false; - } - } else { - // named - abd.addBean(new QualifiedBean<>(OciRestApi.class, - OciRestApi.class, - Set.of(named), - () -> OciRestApi.create(config.get(name)) - )); - } - } - - // now we need to add the full combination of names and types - for (String name : requiredNames) { - OciInternal.Literal qualifier = OciInternal.Literal.create(name); - Named named = NamedLiteral.of(name); - - // for each name - for (InjectionProvider provider : providers) { - // for each provider - for (InjectionProvider.InjectionType injectable : provider.injectables()) { - // for each type - abd.addBean(new QualifiedBean<>(OciCdiExtension.class, - (Class) injectable.injectedType(), - Set.of(named, qualifier), - () -> { - OciRestApi restApi = CDI.current() - .select(OciRestApi.class, named) - .get(); - - return injectable.createInstance(restApi, producerConfig(name)); - })); - } - } - } - } - - private Config producerConfig(String name) { - if (name.isEmpty() || name.equals("default")) { - if (config.get("default").exists()) { - return config.get("default"); - } - return config; - } - return config.get(name); - } -} diff --git a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciInternal.java b/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciInternal.java deleted file mode 100644 index 5a5e1ca109a..00000000000 --- a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/OciInternal.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.cdi; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Objects; - -import jakarta.inject.Qualifier; - -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Qualifier -@interface OciInternal { - String value(); - - class Literal implements OciInternal { - private final String value; - - private Literal(String name) { - this.value = name; - } - - static Literal create(String name) { - return new Literal(name); - } - - @Override - public String value() { - return value; - } - - @Override - public Class annotationType() { - return OciInternal.class; - } - - @Override - public String toString() { - return OciInternal.class.getName() + "(value=\"" + value + "\")"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Literal literal = (Literal) o; - return value.equals(literal.value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - } -} diff --git a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/QualifiedBean.java b/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/QualifiedBean.java deleted file mode 100644 index 8b5ee17c36c..00000000000 --- a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/QualifiedBean.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.cdi; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Supplier; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.context.spi.CreationalContext; -import jakarta.enterprise.inject.Any; -import jakarta.enterprise.inject.Default; -import jakarta.enterprise.inject.spi.Bean; -import jakarta.enterprise.inject.spi.InjectionPoint; -import jakarta.enterprise.inject.spi.PassivationCapable; - -class QualifiedBean implements Bean, PassivationCapable { - private final Class beanClass; - private final Class type; - private final Set qualifiers; - private final Supplier creator; - private final Set customQualifiers; - - QualifiedBean(Class beanClass, Class type, Supplier creator) { - this.beanClass = beanClass; - this.type = type; - this.qualifiers = Set.of(Any.Literal.INSTANCE, Default.Literal.INSTANCE); - this.customQualifiers = Set.of(); - this.creator = creator; - } - - QualifiedBean(Class beanClass, Class type, Set qualifiers, Supplier creator) { - this.beanClass = beanClass; - this.type = type; - Set finalQualifiers = new HashSet<>(qualifiers); - finalQualifiers.add(Any.Literal.INSTANCE); - this.qualifiers = finalQualifiers; - this.customQualifiers = qualifiers; - this.creator = creator; - } - - @Override - public Class getBeanClass() { - return beanClass; - } - - @Override - public Set getInjectionPoints() { - return Set.of(); - } - - @Override - public boolean isNullable() { - return false; - } - - @Override - public T create(CreationalContext creationalContext) { - return creator.get(); - } - - @Override - public void destroy(T instance, CreationalContext creationalContext) { - } - - @Override - public Set getTypes() { - return Set.of(type); - } - - @Override - public Set getQualifiers() { - return qualifiers; - } - - @Override - public Class getScope() { - return ApplicationScoped.class; - } - - @Override - public String getName() { - return type.getName() + "." + qualifiers; - } - - @Override - public Set> getStereotypes() { - return Set.of(); - } - - @Override - public boolean isAlternative() { - return false; - } - - @Override - public String toString() { - return "QualifiedBean{" - + "beanClass=" + beanClass.getSimpleName() - + ", type=" + type.getSimpleName() - + ", qualifiers=" + customQualifiers - + '}'; - } - - @Override - public String getId() { - return getName(); - } -} diff --git a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/package-info.java b/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/package-info.java deleted file mode 100644 index 1344a2056ce..00000000000 --- a/integrations/oci/cdi/src/main/java/io/helidon/integrations/oci/cdi/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * CDI extension to add support for injection of OCI APIs. - */ -package io.helidon.integrations.oci.cdi; diff --git a/integrations/oci/cdi/src/main/java/module-info.java b/integrations/oci/cdi/src/main/java/module-info.java deleted file mode 100644 index c757d658bc3..00000000000 --- a/integrations/oci/cdi/src/main/java/module-info.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -/** - * CDI extension to add support for injection of OCI APIs. - */ -module io.helidon.integrations.oci.cdi { - requires java.logging; - - requires jakarta.cdi; - - requires io.helidon.config; - requires io.helidon.integrations.oci.connect; - requires io.helidon.common.serviceloader; - requires io.helidon.microprofile.cdi; - requires jakarta.inject; - - exports io.helidon.integrations.oci.cdi; - - uses io.helidon.integrations.oci.connect.spi.InjectionProvider; - - provides jakarta.enterprise.inject.spi.Extension - with io.helidon.integrations.oci.cdi.OciCdiExtension; - - opens io.helidon.integrations.oci.cdi to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/cdi/src/main/resources/META-INF/helidon/native-image/weld-proxies.json b/integrations/oci/cdi/src/main/resources/META-INF/helidon/native-image/weld-proxies.json deleted file mode 100644 index 7349e326bea..00000000000 --- a/integrations/oci/cdi/src/main/resources/META-INF/helidon/native-image/weld-proxies.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.connect.OciRestApi" - ] - } -] \ No newline at end of file diff --git a/integrations/oci/cdi/src/main/resources/META-INF/native-image/reflect-config.json b/integrations/oci/cdi/src/main/resources/META-INF/native-image/reflect-config.json deleted file mode 100644 index ab25c58136d..00000000000 --- a/integrations/oci/cdi/src/main/resources/META-INF/native-image/reflect-config.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "name": "io.helidon.integrations.oci.cdi.OciInternal", - "allDeclaredMethods": true, - "allPublicMethods": true - } -] diff --git a/integrations/oci/connect/etc/spotbugs/exclude.xml b/integrations/oci/connect/etc/spotbugs/exclude.xml deleted file mode 100644 index 948c62ef9a3..00000000000 --- a/integrations/oci/connect/etc/spotbugs/exclude.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/integrations/oci/connect/pom.xml b/integrations/oci/connect/pom.xml deleted file mode 100644 index 4576d7583be..00000000000 --- a/integrations/oci/connect/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-connect - Helidon Integrations OCI Connect - - OCI Connectivity and REST API support - - - etc/spotbugs/exclude.xml - - - - - io.helidon.common - helidon-common - - - io.helidon.common - helidon-common-crypto - - - io.helidon.config - helidon-config - - - io.helidon.fault-tolerance - helidon-fault-tolerance - - - io.helidon.integrations.common - helidon-integrations-common-rest - - - io.helidon.webclient - helidon-webclient - - - io.helidon.security - helidon-security - - - io.helidon.webclient - helidon-webclient-security - - - io.helidon.security.providers - helidon-security-providers-http-sign - - - io.helidon.media - helidon-media-jsonp - - - io.helidon.common - helidon-common-key-util - - - io.helidon.security.providers - helidon-security-providers-common - - - io.helidon.security - helidon-security-util - - - io.helidon.config - helidon-config-metadata - provided - true - - - io.helidon.config - helidon-config-metadata-processor - provided - true - - - \ No newline at end of file diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciApiException.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciApiException.java deleted file mode 100644 index 86c9752bf92..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciApiException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import io.helidon.integrations.common.rest.ApiException; - -/** - * OCI integration exception. - * This exception is used when the API invocation fails before we receive an HTTP - * response. - * {@link io.helidon.integrations.oci.connect.OciRestException} is used otherwise. - */ -public class OciApiException extends ApiException { - /** - * Exception without a message and cause. - */ - public OciApiException() { - super(); - } - - /** - * Exception with message. - * - * @param message message - */ - public OciApiException(String message) { - super(message); - } - - /** - * Exception with message and cause. - * - * @param message message - * @param cause cause - */ - public OciApiException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Exception with cause and no message. - * - * @param cause cause - */ - public OciApiException(Throwable cause) { - super(cause); - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigInstancePrincipal.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigInstancePrincipal.java deleted file mode 100644 index 217727ae999..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigInstancePrincipal.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPrivateKey; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.helidon.common.Base64Value; -import io.helidon.common.Version; -import io.helidon.common.configurable.Resource; -import io.helidon.common.crypto.HashDigest; -import io.helidon.common.http.Http; -import io.helidon.common.http.MediaType; -import io.helidon.common.pki.KeyConfig; -import io.helidon.common.reactive.Single; -import io.helidon.media.jsonp.JsonpSupport; -import io.helidon.security.Security; -import io.helidon.webclient.WebClient; -import io.helidon.webclient.WebClientResponse; -import io.helidon.webclient.security.WebClientSecurity; - -import jakarta.json.Json; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; -import jakarta.json.JsonWriterFactory; - -/** - * OCI connectivity configuration based on instance principal. - * This is used when running within OCI VMs. - *

- * Configuration: - * OCI Instance Security - */ -public class OciConfigInstancePrincipal implements OciConfigProvider { - private static final Logger LOGGER = Logger.getLogger(OciConfigInstancePrincipal.class.getName()); - private static final String DEFAULT_METADATA_SERVICE_URL = "http://169.254.169.254/opc/v2/"; - private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Map.of()); - private static final JsonWriterFactory JSON_WRITER_FACTORY = Json.createWriterFactory(Map.of()); - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - private static final HashDigest HASH_DIGEST = HashDigest.create(HashDigest.ALGORITHM_SHA_256); - - private final AtomicReference currentSignatureData = new AtomicReference<>(); - private final String region; - private final String tenancyId; - private final SessionKeys sessionKeys; - private final Supplier> privateKeySupplier; - private final Supplier> certificateSupplier; - private final Supplier> intermediateCertificateSupplier; - private final WebClient federationClient; - private final OciOutboundSecurityProvider securityProvider; - private final AtomicReference> refreshFuture = new AtomicReference<>(); - private final AtomicReference lastSuccessfulRefresh = new AtomicReference<>(Instant.now()); - - private OciConfigInstancePrincipal(Builder builder) { - this.region = builder.region; - this.tenancyId = builder.tenant; - this.sessionKeys = builder.sessionKeys; - this.privateKeySupplier = builder.privateKeySupplier; - this.certificateSupplier = builder.certificateSupplier; - this.intermediateCertificateSupplier = builder.intermediateCertSupplier; - this.federationClient = builder.federationClient; - this.securityProvider = builder.securityProvider; - - // this is blocking intentionally - when starting the service, we want to make sure - // instance principal data is available - this.currentSignatureData.set(getData(sessionKeys.keyPair(), - certificateSupplier.get().await(10, TimeUnit.SECONDS), - intermediateCertificateSupplier.get().await(10, TimeUnit.SECONDS)) - .await(10, TimeUnit.SECONDS)); - } - - // this method blocks when trying to connect to a remote IP address - static boolean isAvailable() { - return WebClient.builder() - .connectTimeout(1, TimeUnit.SECONDS) - .readTimeout(1, TimeUnit.SECONDS) - .baseUri(DEFAULT_METADATA_SERVICE_URL) - .followRedirects(true) - .keepAlive(false) - .build() - .get() - .request() - .map(response -> response.status() == Http.Status.FORBIDDEN_403) - .onErrorResume(it -> false) - .await(); - } - - /** - * Fluent API builder to create customized instances. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create a new instance from environment. - * - * @return a new instance - */ - public static OciConfigInstancePrincipal create() { - return builder().build(); - } - - private static String fingerprint(X509Certificate leafCertificate) { - try { - Base64Value encoded = Base64Value.create(leafCertificate.getEncoded()); - Base64Value digest = HASH_DIGEST.digest(encoded); - return hexEncode(digest.toBytes(), ":"); - } catch (Exception e) { - throw new OciApiException("Failed to get certificate fingerprint " + leafCertificate, e); - } - } - - private static String hexEncode(byte[] bytes, String separator) { - List encoded = new ArrayList<>(bytes.length); - - for (byte aByte : bytes) { - int v = aByte & 0xFF; - encoded.add("" + HEX_ARRAY[v >>> 4] + HEX_ARRAY[v & 0x0F]); - } - - return String.join(separator, encoded); - } - - @Override - public OciSignatureData signatureData() { - return currentSignatureData.get(); - } - - @Override - public String region() { - return region; - } - - @Override - public String tenancyOcid() { - return tenancyId; - } - - @Override - public Single refresh() { - - // we must use future, as there may be multiple singles created from it - CompletableFuture nextFuture = new CompletableFuture<>(); - - CompletionStage inProgress = refreshFuture.compareAndExchange(null, nextFuture); - if (inProgress != null) { - LOGGER.fine("Refresh already in progress."); - // I do not own this refresh - return Single.create(inProgress); - } - - Instant instant = lastSuccessfulRefresh.get(); - if (instant != null && instant.plus(1, ChronoUnit.MINUTES).isAfter(Instant.now())) { - LOGGER.fine("Refresh requested within one minute of last successful refresh, ignoring"); - refreshFuture.set(null); - return Single.just(currentSignatureData.get()); - } - - LOGGER.fine("Refresh of signature data initialized"); - nextFuture.handle((it, throwable) -> { - if (throwable != null) { - LOGGER.fine("Finished refresh with exception: " + throwable.getMessage() + ", stack trace available in FINEST"); - LOGGER.log(Level.FINEST, "Exception", throwable); - } else { - lastSuccessfulRefresh.set(Instant.now()); - LOGGER.fine("Finished refresh successfully. New kid: " + it.keyId()); - } - // now we can open for next refresh command - refreshFuture.set(null); - return null; - }); - - // we may have parallel requests to refresh under heavy load - KeyPair keyPair = sessionKeys.refresh(); - - // trigger read in parallel - Single pkSingle = privateKeySupplier.get(); - Single certSingle = certificateSupplier.get(); - Single intermediateSingle = intermediateCertificateSupplier.get(); - - pkSingle.flatMapSingle(privateKey -> certSingle - .flatMapSingle(certificate -> intermediateSingle - .flatMapSingle(intermediateCert -> refresh(keyPair, privateKey, certificate, intermediateCert)))) - .forSingle(nextFuture::complete) - .exceptionallyAccept(nextFuture::completeExceptionally); - - return Single.create(nextFuture); - } - - private Single refresh(KeyPair keyPair, - PrivateKey privateKey, - X509Certificate leafCertificate, - X509Certificate intermediateCert) { - - OciSignatureData current = currentSignatureData.get(); - - String keyId = tenancyId + "/fed-x509-sha256/" + fingerprint(leafCertificate); - securityProvider.updateSignatureData(OciSignatureData.create(keyId, - (RSAPrivateKey) privateKey)); - - Single response = getData(keyPair, leafCertificate, intermediateCert) - .peek(currentSignatureData::set); - - if (current == null) { - return response; - } else { - // if all fails, return current data - return response.onErrorResume(throwable -> { - LOGGER.log(Level.WARNING, "Failed to refresh instance principal token", throwable); - return current; - }); - } - } - - // only get the new signature data - private Single getData(KeyPair keyPair, - X509Certificate leafCertificate, - X509Certificate intermediateCert) { - - LOGGER.fine("Getting signature data"); - - PublicKey publicKey = keyPair.getPublic(); - - String publicKeyPem = toPem(publicKey); - String leafCertificatePem = toPem(leafCertificate); - String intermediatePem = toPem(intermediateCert); - String purpose = "DEFAULT"; - String fingerprintAlgorithm = "SHA256"; - - JsonObject jsonRequest = JSON.createObjectBuilder() - .add("publicKey", publicKeyPem) - .add("certificate", leafCertificatePem) - .add("intermediateCertificates", JSON.createArrayBuilder().add(intermediatePem)) - .add("purpose", purpose) - .add("fingerprintAlgorithm", fingerprintAlgorithm) - .build(); - - // this requires a content length and hash - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - JSON_WRITER_FACTORY.createWriter(baos).write(jsonRequest); - - byte[] requestBytes = baos.toByteArray(); - String sha256 = HASH_DIGEST.digest(Base64Value.create(requestBytes)).toBase64(); - - return federationClient.post() - .path("/v1/x509") - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON) - .headers(headers -> { - headers.contentLength(requestBytes.length); - headers.add("x-content-sha256", sha256); - return headers; - }) - .submit(requestBytes) - .flatMapSingle(it -> { - if (it.status() - .family() != Http.ResponseStatus.Family.SUCCESSFUL) { - return readError(it); - } - return it.content().as(JsonObject.class); - }) - .map(it -> it.getString("token")) - .map(newToken -> toData(newToken, keyPair)); - } - - private OciSignatureData toData(String newToken, KeyPair keyPair) { - return OciSignatureData.create("ST$" + newToken, (RSAPrivateKey) keyPair.getPrivate()); - } - - private Single readError(WebClientResponse response) { - return response.content() - .as(String.class) - .flatMapSingle(entity -> Single - .error(OciRestException.builder() - .headers(response.headers()) - .status(response.status()) - .message(entity) - .build())); - } - - private String toPem(X509Certificate certificate) { - try { - return Base64.getEncoder().encodeToString(certificate.getEncoded()); - } catch (CertificateEncodingException e) { - throw new OciApiException("Failed to encode certificate " + certificate, e); - } - } - - private String toPem(PublicKey publicKey) { - return Base64.getEncoder().encodeToString(publicKey.getEncoded()); - } - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.connect.OciConfigInstancePrincipal}. - */ - public static class Builder implements io.helidon.common.Builder { - private static final Logger LOGGER = Logger.getLogger(Builder.class.getName()); - private final WebClient.Builder webClientBuilder = WebClient.builder() - .followRedirects(true); - private final WebClient.Builder federationClientBuilder = WebClient.builder() - .followRedirects(true); - - private String metadataServiceUrl = DEFAULT_METADATA_SERVICE_URL; - private WebClient webClient; - private WebClient federationClient; - private String region; - private String federationEndpoint; - private String tenant; - private Supplier> certificateSupplier; - private Supplier> privateKeySupplier; - private Supplier> intermediateCertSupplier; - private SessionKeys sessionKeys; - private OciOutboundSecurityProvider securityProvider; - - private Builder() { - } - - private static Single getPrivateKey(WebClient webClient) { - LOGGER.finest("Looking up private key"); - - return webClient.get() - .path("/identity/key.pem") - .request(String.class) - .map(Builder::toPrivateKey); - } - - private static PrivateKey toPrivateKey(String pemEncoded) { - return KeyConfig.pemBuilder() - .key(Resource.create("identity/key.pem", pemEncoded)) - .build() - .privateKey() - .orElseThrow(() -> new OciApiException("Could not load private key from identity/key.pem")); - } - - private static Single getIntermediateCertificate(WebClient webClient) { - LOGGER.finest("Looking up intermediate certificate"); - - return webClient.get() - .path("/identity/intermediate.pem") - .request(String.class) - .map(Builder::toX509Cert); - } - - private static Single getCertificate(WebClient webClient) { - LOGGER.finest("Looking up certificate"); - - return webClient.get() - .path("/identity/cert.pem") - .request(String.class) - .map(Builder::toX509Cert); - } - - private static X509Certificate toX509Cert(String pemEncoded) { - try { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) factory - .generateCertificate(new ByteArrayInputStream(pemEncoded.getBytes(StandardCharsets.UTF_8))); - } catch (CertificateException e) { - throw new OciApiException("Failed to parse certificate for instance principal", e); - } - } - - @Override - public OciConfigInstancePrincipal build() { - if (webClient == null) { - webClient = webClientBuilder.baseUri(metadataServiceUrl) - .addHeader(Http.Header.ACCEPT, MediaType.TEXT_PLAIN.toString()) - .addHeader(Http.Header.AUTHORIZATION, "Bearer Oracle") - .build(); - } - - // get region - if (region == null) { - region = getRegion(); - } - LOGGER.fine("Using region " + region); - - // endpoint for authentication service - if (federationEndpoint == null) { - federationEndpoint = "https://auth." + region + ".oraclecloud.com"; - } - LOGGER.fine("Using federation endpoint " + federationEndpoint); - - if (certificateSupplier == null) { - certificateSupplier = () -> getCertificate(webClient); - } - - if (privateKeySupplier == null) { - privateKeySupplier = () -> getPrivateKey(webClient); - } - - if (intermediateCertSupplier == null) { - intermediateCertSupplier = () -> getIntermediateCertificate(webClient); - } - if (sessionKeys == null) { - sessionKeys = SessionKeys.create(); - } - - // we need certificate for initial setup - X509Certificate certificate = certificateSupplier.get().await(10, TimeUnit.SECONDS); - - if (tenant == null) { - // now we need certificate and tenancy ID - Map> name = parseName(certificate.getSubjectX500Principal().getName()); - tenant = getOu(name, "opc-tenant:") - .or(() -> getO(name, "opc-identity:")) - .orElseThrow(() -> new OciApiException("Could not identify instance tenant from certificate.")); - - } - - String keyId = tenant + "/fed-x509-sha256/" + fingerprint(certificate); - RSAPrivateKey initialPrivateKey = (RSAPrivateKey) privateKeySupplier.get().await(10, TimeUnit.SECONDS); - this.securityProvider = OciOutboundSecurityProvider.create(OciSignatureData.create(keyId, initialPrivateKey)); - - Security security = Security.builder() - .addOutboundSecurityProvider(securityProvider) - .build(); - - // federation client - if (federationClient == null) { - URI federationUri = URI.create(federationEndpoint); - - federationClient = federationClientBuilder.addMediaSupport(JsonpSupport.create()) - .baseUri(federationEndpoint) - .addHeader("opc-client-info", "Helidon/" + Version.VERSION) - .addHeader("host", federationUri.getHost()) - .addService(WebClientSecurity.create(security)) - .build(); - } - - return new OciConfigInstancePrincipal(this); - } - - /** - * Update web client builder. - * This can be used to configure - * {@link io.helidon.webclient.WebClient.Builder#connectTimeout(long, java.util.concurrent.TimeUnit)}, - * {@link io.helidon.webclient.WebClient.Builder#readTimeout(long, java.util.concurrent.TimeUnit)} and other options. - * - * @param updater consumer that updates the web client builder - * @return updated builder instance - */ - public Builder webClientBuilder(Consumer updater) { - updater.accept(this.webClientBuilder); - return this; - } - - /** - * Configure custom metadata service URL. - * - * @param metadataServiceUrl URL of the service, if not defined, uses {@value #DEFAULT_METADATA_SERVICE_URL} - * @return updated builder - */ - public Builder metadataServiceUrl(String metadataServiceUrl) { - this.metadataServiceUrl = metadataServiceUrl; - return this; - } - - /** - * Configure region to use. - * - * @param region region identifier - * @return updated builder - */ - public Builder region(String region) { - this.region = region; - return this; - } - - /** - * Configure an explicit federation endpoint. If not defined, it is constructed with region. - * - * @param federationEndpoint federation endpoint - * @return updated builder - */ - public Builder federationEndpoint(String federationEndpoint) { - this.federationEndpoint = federationEndpoint; - return this; - } - - private Optional getO(Map> name, String prefix) { - return getFromName(name.get("O"), prefix); - } - - private Optional getOu(Map> name, String prefix) { - return getFromName(name.get("OU"), prefix); - } - - private Optional getFromName(List values, String prefix) { - if (values == null) { - return Optional.empty(); - } - - for (String value : values) { - if (value.startsWith(prefix)) { - return Optional.of(value.substring(prefix.length())); - } - } - - return Optional.empty(); - } - - private Map> parseName(String name) { - Map> result = new HashMap<>(); - //name is a=b,b=c,d=f - String[] parts = name.split(","); - for (String part : parts) { - int equals = part.indexOf('='); - if (equals > 0) { - String key = part.substring(0, equals).trim(); - String value = part.substring(equals + 1).trim(); - - LOGGER.finest("Found name part: " + key + "=" + value); - result.computeIfAbsent(key.toUpperCase(), it -> new LinkedList<>()).add(value); - } else { - LOGGER.fine("Could not understand name part " + part); - } - } - - return result; - } - - private String getRegion() { - LOGGER.finest("Looking up region using " + metadataServiceUrl); - - // returns string, such as eu-frankfurt-1 - return webClient.get() - .path("/instance/region") - .request(String.class) - .await(10, TimeUnit.SECONDS); - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProfile.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProfile.java deleted file mode 100644 index e562e3ffaab..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProfile.java +++ /dev/null @@ -1,520 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.interfaces.RSAPrivateKey; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.logging.Logger; - -import io.helidon.common.Errors; -import io.helidon.common.configurable.Resource; -import io.helidon.common.pki.KeyConfig; -import io.helidon.config.Config; -import io.helidon.config.metadata.Configured; -import io.helidon.config.metadata.ConfiguredOption; - -/** - * OCI configuration required to connect to a service over REST API. - */ -public class OciConfigProfile implements OciConfigProvider { - private final String userOcid; - private final String tenancyOcid; - private final String keyFingerprint; - private final String region; - private final String privateKey; - private final Map fullConfig; - private final OciSignatureData signatureData; - - private OciConfigProfile(Builder builder) { - this.userOcid = builder.userOcid; - this.tenancyOcid = builder.tenancyOcid; - this.keyFingerprint = builder.keyFingerprint; - this.region = builder.region; - this.privateKey = builder.privateKey; - this.fullConfig = Map.copyOf(builder.fullConfig); - this.signatureData = builder.signatureData; - } - - /** - * Create configuration from the default location {@code ~/.oci/config} and - * default profile {@code DEFAULT}. - * - * @return a new configuration loaded from default location - */ - public static OciConfigProfile create() { - return builder().fromOciConfig().build(); - } - - /** - * A new fluent API builder to configure a new profile config. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create a new instance from configuration. - * - * @param config configuration - * @return a new profile configuration - */ - public static OciConfigProfile create(Config config) { - return builder().config(config).build(); - } - - @Override - public OciSignatureData signatureData() { - return signatureData; - } - - /** - * OCID of the user. Uses key {@code user} in OCI config file and in config. - * - * @return user OCID - */ - public String userOcid() { - return userOcid; - } - - - /** - * OCID of the tenancy. Uses key {@code tenancy} in OCI config file and in config. - * - * @return tenancy OCID - */ - @Override - public String tenancyOcid() { - return tenancyOcid; - } - - /** - * Fingerprint of the key. - * Uses key {@code fingerprint} in OCI config file and in config. - * - * @return key fingerprint - */ - public String keyFingerprint() { - return keyFingerprint; - } - - /** - * Region to use when connecting to OCI. This is used to resolve the host to connect to. - * Uses key {@code region} in OCI config file and in config. - * - * @return OCI region, such as {@code eu-frankfurt-1} - */ - @Override - public String region() { - return region; - } - - /** - * Private key (PEM encoded) used to sign requests. - * Uses {@code key-file} in OCI config to locate the PEM file, uses {@code key-pem} in config. - * - * @return private key to use for signing requests - */ - public String privateKey() { - return privateKey; - } - - /** - * A property defined either in OCI config file or in config. - * - * @param key name of the property - * @return value if present in configuration - */ - public Optional property(String key) { - return Optional.ofNullable(fullConfig.get(key)); - } - - /** - * Full map of configuration properties either from OCI config file or from config. - * - * @return map of key/value pairs - */ - public Map fullConfig() { - return fullConfig; - } - - /** - * Fluent API builder for {@link OciConfigProfile}. - */ - @Configured - public static class Builder implements io.helidon.common.Builder { - private static final Logger LOGGER = Logger.getLogger(Builder.class.getName()); - private static final String DEFAULT_PROFILE = "DEFAULT"; - private static final String DEFAULT_PROFILE_PATH = "~/.oci/config"; - - private final Map fullConfig = new HashMap<>(); - - private String userOcid; - private String tenancyOcid; - private String keyFingerprint; - private String region; - private String privateKey; - private OciSignatureData signatureData; - private RSAPrivateKey rsaPrivateKey; - - private Builder() { - } - - @Override - public OciConfigProfile build() { - if (rsaPrivateKey == null) { - this.rsaPrivateKey = KeyConfig.pemBuilder() - .key(Resource.create("PEM encoded private key from profile", privateKey)) - .build() - .privateKey() - .map(RSAPrivateKey.class::cast) - .orElseThrow(() -> new OciApiException("Could not load private key from PEM encoded")); - } - - validate().checkValid(); - - signatureData = OciSignatureData.create(tenancyOcid - + "/" + userOcid - + "/" + keyFingerprint, - rsaPrivateKey); - - return new OciConfigProfile(this); - } - - /** - * Update this builder from configuration. - * - * @param config config located on the node of OCI configuration - * @return updated builder - */ - public Builder config(Config config) { - config.detach().asMap().ifPresent(this.fullConfig::putAll); - - String profile = config.get("profile-name").asString().orElse(DEFAULT_PROFILE); - config.get("profile-file").as(Path.class) - .ifPresentOrElse(value -> fromOciConfig(value, profile), () -> { - // config-file is not present, let's see if we can load defaults - if (config.get("config-file-enabled").asBoolean().orElse(true)) { - fromOciConfig(profile); - } - }); - - config.get("user").asString().ifPresent(this::userOcid); - config.get("fingerprint").asString().ifPresent(this::keyFingerprint); - config.get("tenancy").asString().ifPresent(this::tenancyOcid); - config.get("region").asString().ifPresent(this::region); - config.get("key-pem").asString().ifPresent(this::privateKey); - - return this; - } - - /** - * User OCID. - * - * @param userOcid OCID of the user - * @return updated builder - */ - @ConfiguredOption(key = "user") - public Builder userOcid(String userOcid) { - this.userOcid = userOcid; - this.fullConfig.put("user", userOcid); - - return this; - } - - /** - * Tenancy OCID. - * - * @param tenancyOcid OCID of the tenancy - * @return updated builder - */ - @ConfiguredOption(key = "tenancy") - public Builder tenancyOcid(String tenancyOcid) { - this.tenancyOcid = tenancyOcid; - this.fullConfig.put("tenancy", tenancyOcid); - - return this; - } - - /** - * Key fingerprint. - * - * @param keyFingerprint key fingerprint - * @return updated builder - */ - @ConfiguredOption(key = "fingerprint") - public Builder keyFingerprint(String keyFingerprint) { - this.keyFingerprint = keyFingerprint; - this.fullConfig.put("fingerprint", keyFingerprint); - - return this; - } - - /** - * OCI region. - * - * @param region region - * @return updated builder - */ - @ConfiguredOption - public Builder region(String region) { - this.region = region; - this.fullConfig.put("region", region); - - return this; - } - - /** - * PEM encoded private key. - * - * @param privateKey private key - * @return updated builder - */ - @ConfiguredOption(key = "key-pem") - public Builder privateKey(String privateKey) { - this.privateKey = privateKey; - this.fullConfig.put("key-pem", privateKey); - - return this; - } - - /** - * Configure an explicit RSA private key. - * - * @param privateKey private key to use - * @return updated builder - */ - public Builder privateKey(RSAPrivateKey privateKey) { - this.rsaPrivateKey = privateKey; - - return this; - } - - /** - * Add property as defined in OCI config file. - * - * @param key key - * @param value value - */ - public void property(String key, String value) { - switch (key) { - case "user": - userOcid(value); - break; - case "fingerprint": - keyFingerprint(value); - break; - case "tenancy": - tenancyOcid(value); - break; - case "region": - region(value); - break; - case "key_file": - keyFile(value); - break; - default: - break; - } - - fullConfig.put(key, value); - } - - /** - * Update this builder from OCI configuration on default path with default profile. - * - * @return updated builder - */ - public Builder fromOciConfig() { - return fromOciConfig(defaultPath(), DEFAULT_PROFILE); - } - - /** - * Update this builder from OCI configuration on default path with custom profile. - * - * @param profile name of the profile - * @return updated builder - */ - public Builder fromOciConfig(String profile) { - return fromOciConfig(defaultPath(), profile); - } - - /** - * Update this builder from OCI configuration on custom path with default profile. - * - * @param path path to the profile - * @return updated builder - */ - public Builder fromOciConfig(Path path) { - return fromOciConfig(path, DEFAULT_PROFILE); - } - - /** - * Attempts to read the profile from the OCI config file. - * This method does not fail in case the path is invalid. - * - * @param path path of the OCI config (may not exist) - * @param profile profile to read - * @return updated builder instance - * - * @throws OciRestException in case the file exists, but the profile name is not valid - */ - public Builder fromOciConfig(Path path, String profile) { - if (!Files.exists(path)) { - LOGGER.fine(() -> "OCI config on path " + path.toAbsolutePath() + " does not exist"); - return this; - } - - if (Files.isDirectory(path)) { - LOGGER.warning("OCI config on path " + path.toAbsolutePath() + " is a directory"); - return this; - } - - if (!Files.isReadable(path)) { - LOGGER.warning("OCI config on path " + path.toAbsolutePath() + " is not readable"); - return this; - } - - // now we can process the file - try { - readProfile(path, profile); - } catch (IOException e) { - throw new OciApiException("Failed to read OCI config on path " + path.toAbsolutePath(), e); - } - - return this; - } - - /** - * Whether this builder has the required configuration. - * - * @return {@code true} if this builder is valid - */ - public boolean configured() { - return validate().isValid(); - } - - private void readProfile(Path path, String profile) throws IOException { - List allLines = Files.readAllLines(path); - - boolean foundProfile = false; - - for (String fileLine : allLines) { - String line = fileLine.trim(); - if (line.startsWith("#")) { - continue; - } - if (line.isBlank()) { - continue; - } - if (line.startsWith("[") && line.endsWith("]")) { - String fileProfile = line.substring(1, line.length() - 1); - if (foundProfile) { - // next profile - break; - } - if (fileProfile.equals(profile)) { - foundProfile = true; - } - continue; - } - // ignore other profiles - if (foundProfile) { - // this is (let's hope) a key - int equals = line.indexOf('='); - if (equals < 1) { - LOGGER.warning("Failed to correctly process OCI config " + path - .toAbsolutePath() + ", line " + line + " is not understood"); - continue; - } - String key = line.substring(0, equals).trim(); - String value = line.substring(equals + 1).trim(); - property(key, value); - } - } - if (!foundProfile) { - throw new OciApiException("Did not find profile " + profile + " in OCI config " + path.toAbsolutePath()); - } - } - - private Path defaultPath() { - Path location = Paths.get(System.getProperty("user.home")); - location = location.resolve(".oci").resolve("config"); - - return location; - } - - private void keyFile(String fileName) { - try { - Path path = Paths.get(fileName); - if (Files.exists(path)) { - LOGGER.fine(() -> "Reading private key from file: " + path.toAbsolutePath()); - privateKey(Files.readString(path)); - return; - } - if (fileName.startsWith("~")) { - String homeDir = System.getProperty("user.home"); - String newFileName = homeDir + fileName.substring(1); - Path userHomePath = Paths.get(newFileName); - if (Files.exists(userHomePath)) { - LOGGER.fine(() -> "Reading private key from file: " + userHomePath.toAbsolutePath()); - privateKey(Files.readString(userHomePath)); - return; - } - } - throw new IllegalArgumentException("Private key file " + fileName + " does not exist"); - } catch (IOException e) { - throw new IllegalStateException("Failed to read private key file " + fileName, e); - } - } - - private Errors validate() { - Errors.Collector collector = Errors.collector(); - - if (userOcid == null) { - collector.fatal("\"user\" key not defined. It should contain the OCID of user"); - } - - if (tenancyOcid == null) { - collector.fatal("\"tenancy\" key not defined. It should contain the OCID of tenancy"); - } - - if (keyFingerprint == null) { - collector.fatal("\"fingerprint\" key not defined. It should contain the key fingerprint"); - } - - if (region == null) { - collector.fatal("\"region\" key not defined. It should contain the OCI region"); - } - - if (privateKey == null && rsaPrivateKey == null) { - collector.fatal("\"key_file\" key not defined. It should provide location of the private key PEM file," - + "or an RSA private key should be explicitly configured"); - } - - return collector.collect(); - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProvider.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProvider.java deleted file mode 100644 index d49100a9653..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.util.Optional; - -import io.helidon.common.reactive.Single; - -/** - * Provider of data needed to connect to OCI, and to use its APIs. - */ -public interface OciConfigProvider { - /** - * Get the current signature data. - * - * @return current signature data - */ - OciSignatureData signatureData(); - - /** - * Current OCI region. - * - * @return OCI region, such as {@code eu-frankfurt-1} - */ - String region(); - - /** - * OCID of the tenancy. - * - * @return tenancy OCID - */ - String tenancyOcid(); - - /** - * OCI domain to use. If not available REST API will use the default or configured domain to - * call REST services. - * - * @return current OCI domain if avialable - */ - default Optional domain() { - return Optional.empty(); - } - - /** - * Refresh may be used for providers that can be reloaded. - * The method is called when an invocation fails (for the first time) with a 401 exception. - * - * @return future with signature data, if no refresh was done, the instance will be the same as when - * {@link #signatureData()} was called. - */ - default Single refresh() { - return Single.just(signatureData()); - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigResourcePrincipal.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigResourcePrincipal.java deleted file mode 100644 index 0f6ad2c80c1..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciConfigResourcePrincipal.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.util.Map; - -import io.helidon.common.reactive.Single; - -/** - * OCI connectivity configuration based on resource principal. - * This is used when running within functions. - * - * TODO THIS CLASS IS NOT YET IMPLEMENTED AND WILL THROW AN EXCEPTION - */ -public class OciConfigResourcePrincipal implements OciConfigProvider { - private static final String OCI_RESOURCE_PRINCIPAL_VERSION = "OCI_RESOURCE_PRINCIPAL_VERSION"; - private static final String RP_VERSION_2_2 = "2.2"; - private static final String OCI_RESOURCE_PRINCIPAL_RPST = "OCI_RESOURCE_PRINCIPAL_RPST"; - private static final String OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM = "OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM"; - private static final String OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM_PASSPHRASE = - "OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM_PASSPHRASE"; - private static final String OCI_RESOURCE_PRINCIPAL_REGION_ENV_VAR_NAME = - "OCI_RESOURCE_PRINCIPAL_REGION"; - - /** - * Return true if the current environment seems to support resource principal. - * - * @return {@code true} if this environment can use resource principal - */ - static boolean isAvailable() { - Map env = System.getenv(); - - // this is not complete, but it is a fair guess at the availability - // the variables may be wrong - that will fail the process when we try to set everything up - return env.containsKey(OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM) - && env.containsKey(OCI_RESOURCE_PRINCIPAL_RPST) - && env.containsKey(OCI_RESOURCE_PRINCIPAL_REGION_ENV_VAR_NAME); - } - - /** - * Create a new instance from environment. - * - * @return new config provider - */ - public static OciConfigResourcePrincipal create() { - throw new UnsupportedOperationException("OCI resource principal authentication is not yet supported"); - } - - @Override - public OciSignatureData signatureData() { - return null; - } - - @Override - public String region() { - return null; - } - - @Override - public String tenancyOcid() { - return null; - } - - @Override - public Single refresh() { - return OciConfigProvider.super.refresh(); - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciHttpSignature.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciHttpSignature.java deleted file mode 100644 index f03285a632c..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciHttpSignature.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.security.interfaces.RSAPrivateKey; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.helidon.common.Base64Value; -import io.helidon.common.crypto.Signature; -import io.helidon.security.SecurityEnvironment; -import io.helidon.security.providers.httpsign.HttpSignatureException; -import io.helidon.security.providers.httpsign.SignedHeadersConfig; - -/** - * Class wrapping signature and fields needed to build and validate it. - */ -class OciHttpSignature { - private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.RFC_1123_DATE_TIME; - private static final Logger LOGGER = Logger.getLogger(OciHttpSignature.class.getName()); - private static final String ALGORITHM = "rsa-sha256"; - - private final String keyId; - private final List headers; - private String base64Signature; - - OciHttpSignature(String keyId, List headers) { - this.keyId = keyId; - this.headers = headers; - } - - static OciHttpSignature sign(SignatureRequest request) { - OciHttpSignature signature = new OciHttpSignature(request.keyId, - request.headersToSign); - - Base64Value signedValue = signature.signRsaSha256(request.env, - request.privateKey, - request.newHeaders); - signature.base64Signature = signedValue.toBase64(); - - return signature; - } - - String toSignatureHeader() { - return "keyId=\"" + keyId + "\"," - + "algorithm=\"" + ALGORITHM + "\"," - + "headers=\"" + String.join(" ", headers) + "\"," - + "signature=\"" + base64Signature + "\""; - } - - private Base64Value signRsaSha256(SecurityEnvironment env, RSAPrivateKey privateKey, Map> newHeaders) { - Signature signature = Signature.builder() - .privateKey(privateKey) - .algorithm(Signature.ALGORITHM_SHA256_RSA) - .build(); - - return signature.digest(Base64Value.create(getBytesToSign(env, newHeaders))); - } - - private byte[] getBytesToSign(SecurityEnvironment env, Map> newHeaders) { - return getSignedString(newHeaders, env).getBytes(StandardCharsets.UTF_8); - } - - String getSignedString(Map> newHeaders, SecurityEnvironment env) { - Map> requestHeaders = env.headers(); - List linesToSign = new LinkedList<>(); - - for (String header : this.headers) { - if ("(request-target)".equals(header)) { - //special case - linesToSign.add(header - + ": " + env.method().toLowerCase() - + " " + env.path().orElse("/")); - } else { - List headerValues = requestHeaders.get(header); - if (null == headerValues && null == newHeaders) { - // we do not support creation of new headers, just throw an exception - throw new HttpSignatureException("Header " + header + " is required for signature, yet not defined in " - + "request"); - } - if (null == headerValues) { - // there are two headers we understand and may want to add to request - if ("date".equalsIgnoreCase(header)) { - String date = ZonedDateTime.now(ZoneId.of("GMT")).format(DATE_FORMATTER); - headerValues = List.of(date); - newHeaders.put("date", headerValues); - - LOGGER.finest(() -> "Added date header to request: " + date); - } else if ("host".equalsIgnoreCase(header)) { - URI uri = env.targetUri(); - - String host = uri.getHost() + ":" + uri.getPort(); - headerValues = List.of(host); - newHeaders.put("host", headerValues); - - LOGGER.finest(() -> "Added host header to request: " + host); - } else { - throw new HttpSignatureException("Header " + header + " is required for signature, yet not defined in " - + "request"); - } - } - - linesToSign.add(header + ": " + String.join(" ", headerValues)); - } - } - - // 2.3. Signature String Construction - // If value is not the last value then append an ASCII newline `\n`. - String toSign = String.join("\n", linesToSign); - - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("Data to sign: " + toSign); - } - - return toSign; - } - - public static class SignatureRequest { - private final SecurityEnvironment env; - private final Map> newHeaders; - private final String keyId; - private final List headersToSign; - private final RSAPrivateKey privateKey; - - private SignatureRequest(Builder builder) { - this.env = builder.env; - this.newHeaders = builder.newHeaders; - this.keyId = builder.keyId; - this.headersToSign = builder.headersToSign; - this.privateKey = builder.privateKey; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder implements io.helidon.common.Builder { - private RSAPrivateKey privateKey; - private SecurityEnvironment env; - private Map> newHeaders; - private String keyId; - private SignedHeadersConfig headersConfig; - private List headersToSign; - - private Builder() { - } - - @Override - public SignatureRequest build() { - this.headersToSign = headersConfig.headers(env.method(), env.headers()); - return new SignatureRequest(this); - } - - public Builder env(SecurityEnvironment env) { - this.env = env; - return this; - } - - public Builder newHeaders(Map> newHeaders) { - this.newHeaders = newHeaders; - return this; - } - - public Builder keyId(String keyId) { - this.keyId = keyId; - return this; - } - - public Builder headersConfig(SignedHeadersConfig headersConfig) { - this.headersConfig = headersConfig; - return this; - } - - public Builder privateKey(RSAPrivateKey privateKey) { - this.privateKey = privateKey; - return this; - } - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciOutboundSecurityProvider.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciOutboundSecurityProvider.java deleted file mode 100644 index afcf08e9ed7..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciOutboundSecurityProvider.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Logger; - -import io.helidon.common.reactive.Single; -import io.helidon.integrations.oci.connect.OciHttpSignature.SignatureRequest; -import io.helidon.security.EndpointConfig; -import io.helidon.security.OutboundSecurityResponse; -import io.helidon.security.ProviderRequest; -import io.helidon.security.SecurityEnvironment; -import io.helidon.security.SecurityException; -import io.helidon.security.SecurityResponse; -import io.helidon.security.providers.common.OutboundConfig; -import io.helidon.security.providers.common.OutboundTarget; -import io.helidon.security.providers.httpsign.SignedHeadersConfig; -import io.helidon.security.spi.OutboundSecurityProvider; -import io.helidon.security.util.TokenHandler; - -class OciOutboundSecurityProvider implements OutboundSecurityProvider { - private static final SignedHeadersConfig SIGNED_HEADERS = SignedHeadersConfig.builder() - .defaultConfig(SignedHeadersConfig.HeadersConfig - .create(List.of("date", SignedHeadersConfig.REQUEST_TARGET, "host"))) - .config("put", SignedHeadersConfig.HeadersConfig - .create(List.of(SignedHeadersConfig.REQUEST_TARGET, - "host", - "date"), - List.of( - "x-content-sha256", - "content-type", - "content-length"))) - .config("post", SignedHeadersConfig.HeadersConfig - .create(List.of(SignedHeadersConfig.REQUEST_TARGET, - "host", - "date"), - List.of( - "x-content-sha256", - "content-type", - "content-length"))) - .build(); - - private static final SignedHeadersConfig OBJECT_STORAGE_UPLOAD_SIGNED_HEADERS = SignedHeadersConfig.builder() - .defaultConfig(SignedHeadersConfig.HeadersConfig - .create(List.of("date", SignedHeadersConfig.REQUEST_TARGET, "host"))) - .build(); - - private static final TokenHandler TOKEN_HANDLER = TokenHandler.builder() - .tokenPrefix("Signature version=\"1\",") - .tokenHeader("Authorization") - .build(); - - private static final Logger LOGGER = Logger.getLogger(OciOutboundSecurityProvider.class.getName()); - - private final AtomicReference signatureData = new AtomicReference<>(); - private final OutboundConfig outboundConfig; - - OciOutboundSecurityProvider(OciSignatureData signatureData) { - this.signatureData.set(signatureData); - this.outboundConfig = outboundConfig(); - } - - static OciOutboundSecurityProvider create(OciSignatureData signatureData) { - return new OciOutboundSecurityProvider(signatureData); - } - - private static OutboundConfig outboundConfig() { - return OutboundConfig.builder() - .addTarget(outboundTargetUpload()) - .addTarget(outboundTargetFull()) - .build(); - } - - private static OutboundTarget outboundTargetUpload() { - return OutboundTarget.builder("oci-object-storage") - .customObject(SignatureTarget.class, SignatureTarget.create(OBJECT_STORAGE_UPLOAD_SIGNED_HEADERS)) - .addMethod("PUT") - .addHost("objectstorage.*") - .addHost("*") - .addPath("/n/.+/b/.+/o/.+") - .build(); - } - - private static OutboundTarget outboundTargetFull() { - return OutboundTarget.builder("oci") - .customObject(SignatureTarget.class, SignatureTarget.create(SIGNED_HEADERS)) - .addHost("*") - .build(); - } - - @Override - public CompletionStage outboundSecurity(ProviderRequest providerRequest, - SecurityEnvironment outboundEnv, - EndpointConfig outboundConfig) { - return Single.just(sign(outboundEnv)); - } - - void updateSignatureData(OciSignatureData data) { - this.signatureData.set(data); - } - - // we are not running any asynchronous stuff in here, we can just return synchronously - private OutboundSecurityResponse sign(SecurityEnvironment outboundEnv) { - return this.outboundConfig.findTarget(outboundEnv) - .map(it -> sign(outboundEnv, it)) - .orElseGet(OutboundSecurityResponse::empty); - } - - private OutboundSecurityResponse sign(SecurityEnvironment outboundEnv, OutboundTarget target) { - SignatureTarget signatureTarget = target.customObject(SignatureTarget.class) - .orElseThrow(() -> new SecurityException("Failed to find signature configuration for target " + target.name())); - - Map> newHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - newHeaders.putAll(outboundEnv.headers()); - - OciSignatureData sigData = signatureData.get(); - LOGGER.finest("Creating request signature with kid: " + sigData.keyId()); - - OciHttpSignature signature = OciHttpSignature.sign(SignatureRequest.builder() - .env(outboundEnv) - .privateKey(sigData.privateKey()) - .keyId(sigData.keyId()) - .headersConfig(signatureTarget.signedHeadersConfig) - .newHeaders(newHeaders) - .build()); - - TOKEN_HANDLER.addHeader(newHeaders, signature.toSignatureHeader()); - - return OutboundSecurityResponse.builder() - .requestHeaders(newHeaders) - .status(SecurityResponse.SecurityStatus.SUCCESS) - .build(); - } - - private static class SignatureTarget { - private final SignedHeadersConfig signedHeadersConfig; - - private SignatureTarget(SignedHeadersConfig signedHeadersConfig) { - this.signedHeadersConfig = signedHeadersConfig; - } - - private static SignatureTarget create(SignedHeadersConfig signedHeaders) { - return new SignatureTarget(signedHeaders); - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRequestBase.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRequestBase.java deleted file mode 100644 index f62dd5b065a..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRequestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.util.Optional; - -import io.helidon.integrations.common.rest.ApiJsonRequest; - -/** - * A base for OCI requests that acts as a builder. - * Adds support for {@link #retryToken}. - * - * @param type of the request - */ -public abstract class OciRequestBase> extends ApiJsonRequest { - private static final DateTimeFormatter INSTANT_FORMATTER = new DateTimeFormatterBuilder() - .parseCaseInsensitive() - .appendInstant(3) - .toFormatter(); - - private String hostFormat; - private String retryToken; - private String hostPrefix; - private String endpoint; - - protected OciRequestBase() { - } - - /** - * Retry token to support idempotent request when updating data. - * - * @param retryToken retry token - * @return updated request - */ - public T retryToken(String retryToken) { - this.retryToken = retryToken; - return me(); - } - - /** - * Host prefix to use. This is intended for API implementation and allows override of - * the default host prefix (for example in Vault API, we may use two different prefixes). - * - * @param hostPrefix host prefix - * @return updated request - */ - public T hostPrefix(String hostPrefix) { - if (this.hostPrefix == null) { - this.hostPrefix = hostPrefix; - } - return me(); - } - - /** - * Host format to use. - * Domain specific APIs must define a host format for each request. - * - * @param hostFormat host format - * @return updated request - */ - public T hostFormat(String hostFormat) { - if (this.hostFormat == null) { - this.hostFormat = hostFormat; - } - return me(); - } - - /** - * Override the endpoint to use for this request. - * - * @param endpoint full endpoint to override the default - * @return updated builder - */ - public T endpoint(String endpoint) { - if (this.endpoint == null) { - this.endpoint = endpoint; - } - return me(); - } - - /** - * Add a timestamp to the JSON. Uses the timestamp as defined in OCI API. - * - * @param name name of the property - * @param instant instant value - * @return updated request - */ - protected T add(String name, Instant instant) { - return super.add(name, INSTANT_FORMATTER.format(instant)); - } - - String hostPrefix() { - if (hostPrefix == null) { - throw new OciApiException("Host prefix must be defined to find correct OCI host."); - } - return hostPrefix; - } - - /** - * Endpoint (if configured). - * - * @return configured endpoint or empty - */ - public Optional endpoint() { - return Optional.ofNullable(endpoint); - } - - Optional retryToken() { - return Optional.ofNullable(retryToken); - } - - String hostFormat() { - if (hostFormat == null) { - throw new OciApiException("Host format must be defined to resolve OCI address"); - } - return hostFormat; - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciResponseParser.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciResponseParser.java deleted file mode 100644 index ee0cc3668d8..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciResponseParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; -import java.util.Optional; - -import io.helidon.integrations.common.rest.ApiJsonParser; - -import jakarta.json.JsonObject; - -/** - * Utility methods for processing OCI JSON responses. - */ -public abstract class OciResponseParser extends ApiJsonParser { - private static final DateTimeFormatter INSTANT_PARSER = DateTimeFormatter.ISO_INSTANT; - - /** - * New instance. - */ - protected OciResponseParser() { - } - - /** - * Get instant from JSON using a parser that understands OCI date time format. - * - * @param json JSON object - * @param name property of the object - * @return instant parsed from the string value of the property - */ - protected Instant getInstant(JsonObject json, String name) { - TemporalAccessor accessor = INSTANT_PARSER.parse(json.getString(name)); - return Instant.from(accessor); - } - - /** - * Get instant from JSON using a parser that understands OCI date time format. - * - * @param json JSON object - * @param name property of the object - * @return instant parsed from the string value of the property, or empty if not present or null - */ - protected Optional toInstant(JsonObject json, String name) { - return toInstant(json, name, INSTANT_PARSER); - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestApi.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestApi.java deleted file mode 100644 index 2342707622c..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestApi.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Optional; -import java.util.concurrent.Flow; -import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.helidon.common.Base64Value; -import io.helidon.common.Version; -import io.helidon.common.crypto.HashDigest; -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.http.MediaType; -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.integrations.common.rest.ApiRequest; -import io.helidon.integrations.common.rest.RestApi; -import io.helidon.integrations.common.rest.RestApiBase; -import io.helidon.security.Security; -import io.helidon.webclient.WebClientRequestBuilder; -import io.helidon.webclient.WebClientResponse; -import io.helidon.webclient.security.WebClientSecurity; - -import jakarta.json.JsonObject; - -/** - * OCI specific REST API. - * This class uses HTTP Signatures to sign requests. - */ -public class OciRestApi extends RestApiBase { - private static final Logger LOGGER = Logger.getLogger(OciRestApi.class.getName()); - private static final HashDigest HASH_DIGEST = HashDigest.create(HashDigest.ALGORITHM_SHA_256); - - private final BiFunction formatFunction; - private final OciOutboundSecurityProvider outboundProvider; - private final OciConfigProvider configProvider; - - private OciRestApi(Builder builder) { - super(builder); - - this.formatFunction = builder.formatFunction; - this.outboundProvider = builder.outboundProvider; - this.configProvider = builder.ociConfigProvider; - } - - /** - * A new builder to configure instances. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create API with default configuration. - * - * @return a new REST API with default configuration - */ - public static OciRestApi create() { - return builder().build(); - } - - /** - * Create OCI REST API from configuration. - * - * @param config configuration - * @return a new REST API configured from config - */ - public static OciRestApi create(Config config) { - return builder().config(config).build(); - } - - private static ConfigType guessConfigType() { - // attempt to guess what config type this is, let's start from the most "obscure" one and - // end with local environment - if (OciConfigResourcePrincipal.isAvailable()) { - LOGGER.fine("OCI Resource Principal configuration is available."); - return ConfigType.RESOURCE_PRINCIPAL; - } - if (OciConfigInstancePrincipal.isAvailable()) { - LOGGER.fine("OCI Instance Principal configuration is available."); - return ConfigType.INSTANCE_PRINCIPAL; - } - - LOGGER.fine("Using OCI Profile based configuration, as neither resource nor instance principal is available"); - return ConfigType.OCI_PROFILE; - } - - @Override - protected Supplier> responseSupplier(Http.RequestMethod method, - String path, - ApiRequest request, - String requestId) { - Supplier> originalSupplier = super.responseSupplier(method, path, request, requestId); - - return wrapSupplierInSecurityRetry(originalSupplier); - } - - @Override - protected Supplier> requestBytesPayload(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientRequestBuilder requestBuilder, - Flow.Publisher publisher) { - requestBuilder.allowChunkedEncoding(false); - Supplier> originalSupplier = super - .requestBytesPayload(path, request, method, requestId, requestBuilder, publisher); - - return wrapSupplierInSecurityRetry(originalSupplier); - } - - private Supplier> - wrapSupplierInSecurityRetry(Supplier> originalSupplier) { - // to handle timed-out token(s) when using instance or resource identity, we need to retry in case we get 401 - // and the token refreshes successfully - return () -> { - OciSignatureData ociSignatureData = configProvider.signatureData(); - LOGGER.finest("Trying request with kid: " + ociSignatureData.keyId()); - - return originalSupplier.get() - .flatMapSingle(clientResponse -> { - if (clientResponse.status() == Http.Status.UNAUTHORIZED_401) { - // maybe this is a timed-out token - LOGGER.finest("Unauthorized request, trying to refresh signature data"); - return configProvider.refresh() - .flatMapSingle(newSignatureData -> { - if (newSignatureData == ociSignatureData) { - // no change in signature data, just return - LOGGER.finest("Signature data refresh failed, failing API request"); - return Single.just(clientResponse); - } else { - // signature data modified, let's retry - LOGGER.finest("Signature data refresh succeeded, retrying with kid: " - + newSignatureData.keyId()); - outboundProvider.updateSignatureData(newSignatureData); - return originalSupplier.get(); - } - }); - } else { - return Single.just(clientResponse); - } - }); - }; - } - - @Override - protected Throwable readErrorFailedEntity(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientResponse response, - Throwable it) { - String messagePrefix = "Failed to " + method + " on path " + path + " " + response.status().code() + " " + response; - OciRestException.Builder errorBuilder = OciRestException.builder() - .cause(it) - .requestId(requestId) - .headers(response.headers()) - .status(response.status()); - return errorBuilder - .message(messagePrefix + ". Failed to read OCI error, failed with: " + it.getMessage()) - .apiSpecificError("Unknown.") - .build(); - } - - @Override - protected Throwable readError(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientResponse response) { - - String messagePrefix = "Failed to " + method + " on path " + path + " " + response.status().code(); - OciRestException.Builder errorBuilder = OciRestException.builder() - .requestId(requestId) - .headers(response.headers()) - .status(response.status()); - return errorBuilder - .message(messagePrefix + ". Failed to read OCI error, no response entity") - .apiSpecificError("Unknown.") - .build(); - } - - @Override - protected Throwable readError(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientResponse response, - String entity) { - String messagePrefix = "Failed to " + method + " on path " + path + " " + response.status().code(); - OciRestException.Builder errorBuilder = OciRestException.builder() - .requestId(requestId) - .headers(response.headers()) - .status(response.status()); - return errorBuilder - .message(messagePrefix + ". Failed to read OCI error, original entity: " + URLEncoder - .encode(entity, StandardCharsets.UTF_8)) - .apiSpecificError("Unknown.") - .build(); - } - - @Override - protected Throwable readError(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientResponse response, - JsonObject json) { - - String messagePrefix = "Failed to " + method + " on path " + path + " " + response.status(); - OciRestException.Builder errorBuilder = OciRestException.builder() - .requestId(requestId) - .headers(response.headers()) - .status(response.status()); - - try { - String errorCode = json.getString("code"); - String errorMessage = json.getString("message"); - return errorBuilder - .message(messagePrefix + ". OCI code: " + errorCode + ", OCI message: " + errorMessage) - .apiSpecificError("OCI code: " + errorCode + ", OCI message: " + errorMessage) - .errorCode(errorCode) - .errorMessage(errorMessage) - .build(); - } catch (Exception e) { - LOGGER.log(Level.FINE, "Failed to read error response", e); - return errorBuilder - .cause(e) - .message(messagePrefix + ". Failed to read OCI error, original entity: " + URLEncoder - .encode(json.toString(), StandardCharsets.UTF_8)) - .apiSpecificError("Unknown.") - .build(); - } - } - - @Override - protected Single updateRequestBuilder(WebClientRequestBuilder requestBuilder, - String path, - ApiRequest request, - Http.RequestMethod method, - String requestId) { - // signatures not needed - updateRequestBuilder(requestBuilder, request, requestId); - return Single.just(requestBuilder); - } - - @Override - protected Single updateRequestBuilderBytesPayload(WebClientRequestBuilder requestBuilder, - String path, - ApiRequest request, - Http.RequestMethod method, - String requestId) { - // signatures not needed - updateRequestBuilder(requestBuilder, request, requestId); - return Single.just(requestBuilder); - } - - @Override - protected Supplier> requestJsonPayload(String path, - ApiRequest request, - Http.RequestMethod method, - String requestId, - WebClientRequestBuilder requestBuilder, - JsonObject jsonObject) { - - requestBuilder.accept(request.responseMediaType().orElse(MediaType.APPLICATION_JSON)); - requestBuilder.contentType(request.requestMediaType().orElse(MediaType.APPLICATION_JSON)); - // common stuff - updateRequestBuilder(requestBuilder, request, requestId); - // signature requirement - - // this requires a content length and hash - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - jsonWriterFactory().createWriter(baos).write(jsonObject); - - byte[] requestBytes = baos.toByteArray(); - String sha256 = HASH_DIGEST.digest(Base64Value.create(requestBytes)).toBase64(); - requestBuilder.headers(headers -> { - headers.contentLength(requestBytes.length); - headers.add("x-content-sha256", sha256); - return headers; - }).contentType(request.requestMediaType().orElse(MediaType.APPLICATION_JSON)); - - return () -> requestBuilder.submit(requestBytes); - } - - @Override - protected Single updateRequestBuilderCommon(WebClientRequestBuilder requestBuilder, - String path, - ApiRequest request, - Http.RequestMethod method, - String requestId) { - updateRequestBuilder(requestBuilder, request, requestId); - return Single.just(requestBuilder); - } - - private void updateRequestBuilder(WebClientRequestBuilder requestBuilder, ApiRequest request, String requestId) { - requestBuilder.headers().add("opc-request-id", requestId); - - if (request instanceof OciRequestBase) { - OciRequestBase ociRequest = (OciRequestBase) request; - ociRequest.retryToken().ifPresent(it -> requestBuilder.headers().add("opc-retry-token", it)); - Optional maybeAddress = ociRequest.endpoint(); - URI ociUri; - - if (maybeAddress.isPresent()) { - String address = maybeAddress.get(); - requestBuilder.uri(address); - ociUri = URI.create(address); - } else { - String prefix = ociRequest.hostPrefix(); - String address = formatFunction.apply(ociRequest.hostFormat(), prefix); - requestBuilder.uri(address); - ociUri = URI.create(address); - } - - // this is a required change - Host was failing with secret bundles, host works - requestBuilder.headers().add("host", ociUri.getHost()); - } else { - throw new OciApiException("Cannot handle non-OCI requests. Request: " + request.getClass().getName()); - } - } - - /** - * How to connect to OCI. - */ - public enum ConfigType { - /** - * Use {@link io.helidon.integrations.oci.connect.OciConfigInstancePrincipal}. - */ - INSTANCE_PRINCIPAL, - /** - * Use {@link io.helidon.integrations.oci.connect.OciConfigResourcePrincipal}. - */ - RESOURCE_PRINCIPAL, - /** - * Use {@link io.helidon.integrations.oci.connect.OciConfigProfile}. - */ - OCI_PROFILE - } - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.connect.OciRestApi}. - *

- * The final host of each endpoint is computed based on a format provided by each request (usually the - * same format for a certain area, such as Vault, ObjectStorage etc.), and a prefix also received with each - * request. - *

- * The template {@code %s://%s.%s.%s} would resolve into - * {@code ${scheme}://${hostPrefix}.${region}.oci.${domain}}. - *

- * Let's consider the following configuration: - *

    - *
  • {@code scheme}: {@value #DEFAULT_DOMAIN}
  • - *
  • {@code hostPrefix}: {@code vaults}
  • - *
  • {@code region}: {@code eu-frankfurt-1}
  • - *
  • {@code domain}: {@value #DEFAULT_DOMAIN}
  • - *
- * we would get {@code https://vaults.eu-frankfurt-1.oraclecloud.com} as the endpoint. - * - * In case we need to connect to a local docker image, or a testing environment, an explicit - * address can be configured for each domain specific API, or parts of the template can be modified - * if the final address matches the expected structure. - */ - public static class Builder extends RestApi.Builder { - private static final String DEFAULT_SCHEME = "https"; - private static final String DEFAULT_DOMAIN = "oraclecloud.com"; - - private ConfigType configType = guessConfigType(); - - private OciConfigProvider ociConfigProvider; - private BiFunction formatFunction; - private String scheme = DEFAULT_SCHEME; - private String domain = DEFAULT_DOMAIN; - private OciOutboundSecurityProvider outboundProvider; - - private Builder() { - } - - /** - * Update builder from configuration. - * - * @param config config located on the node of OCI configuration - * @return updated builder - */ - public Builder config(Config config) { - super.config(config); - - config.get("scheme").asString().ifPresent(this::scheme); - config.get("domain").asString().ifPresent(this::domain); - - Config configProviderConfig = config.get("config"); - - Config instancePrincipal = configProviderConfig.get("instance-principal"); - Config resourcePrincipal = configProviderConfig.get("resource-principal"); - Config profileConfig = configProviderConfig.get("oci-profile"); - - if (instancePrincipal.exists() && instancePrincipal.get("enabled").asBoolean().orElse(true)) { - // use instance principal - configType = ConfigType.INSTANCE_PRINCIPAL; - } else if (resourcePrincipal.exists() && resourcePrincipal.get("enabled").asBoolean().orElse(true)) { - // use resource principal - configType = ConfigType.RESOURCE_PRINCIPAL; - } - if (profileConfig.exists() && profileConfig.get("enabled").asBoolean().orElse(true)) { - // use config profile - configType = ConfigType.OCI_PROFILE; - configProvider(OciConfigProfile.create(profileConfig)); - } - - return this; - } - - /** - * How to obtain data to configure connectivity to OCI. - * By default, an attempt is made to discover whether to use - * instance principal or resource principal. Falls back to {@link ConfigType#OCI_PROFILE}. - * - * @param configType type of config to use - * @return updated builder - */ - public Builder configType(ConfigType configType) { - this.configType = configType; - return this; - } - - /** - * Scheme to use when constructing endpoint address. - * Defaults to {@value #DEFAULT_SCHEME}. - * - * @param scheme scheme to use (most likely either {@code http} or {@code https} - * @return updated builder - */ - public Builder scheme(String scheme) { - this.scheme = scheme; - return this; - } - - /** - * Domain to use when constructing endpoint address. - * Defaults to {@value #DEFAULT_DOMAIN}. - * - * @param domain domain to use - * @return updated builder - */ - public Builder domain(String domain) { - this.domain = domain; - return this; - } - - /** - * Cloud connectivity configuration to use. - * - * @param ociConfigProvider profile config, such as {@link OciConfigProfile} - * @return updated builder - */ - public Builder configProvider(OciConfigProvider ociConfigProvider) { - this.ociConfigProvider = ociConfigProvider; - return this; - } - - @Override - protected void preBuild() { - if (ociConfigProvider == null) { - LOGGER.finest("Config provider is not configured explicitly. Config type: " + configType); - switch (configType) { - case INSTANCE_PRINCIPAL: - configProvider(OciConfigInstancePrincipal.create()); - break; - case RESOURCE_PRINCIPAL: - configProvider(OciConfigResourcePrincipal.create()); - break; - case OCI_PROFILE: - default: - configProvider(OciConfigProfile.create()); - break; - } - } - - String scheme = this.scheme; - String region = this.ociConfigProvider.region(); - String domain = this.ociConfigProvider.domain().orElse(this.domain); - - formatFunction = (format, hostPrefix) -> String.format(format, scheme, hostPrefix, region, domain); - - this.outboundProvider = OciOutboundSecurityProvider.create(ociConfigProvider.signatureData()); - - // this must happen only once - webClientSecurity(); - - // this method creates a client instance, must be after we set up client security - super.preBuild(); - } - - @Override - protected OciRestApi doBuild() { - return new OciRestApi(this); - } - - private void webClientSecurity() { - Security security = Security.builder() - .addOutboundSecurityProvider(outboundProvider) - .build(); - - super.webClientBuilder(builder -> { - builder.addHeader("opc-client-info", "Helidon/" + Version.VERSION); - builder.addService(WebClientSecurity.create(security)); - }); - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestException.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestException.java deleted file mode 100644 index 27781a0616c..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciRestException.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.util.Optional; - -import io.helidon.integrations.common.rest.ApiRestException; - -/** - * Exception used when OCI REST call returned and we have HTTP status and headers, and possibly an entity. - */ -public class OciRestException extends ApiRestException { - private final String errorCode; - private final String errorMessage; - - private OciRestException(Builder builder) { - super(builder); - - this.errorCode = builder.errorCode; - this.errorMessage = builder.errorMessage; - } - - /** - * Create a new builder for this exception. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * OCI error code (if present in response). - * - * @return OCI error code - */ - public Optional ociCode() { - return Optional.ofNullable(errorCode); - } - - /** - * OCI error message (if present in response). - * - * @return OCI error message - */ - public Optional ociMessage() { - return Optional.ofNullable(errorMessage); - } - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.connect.OciRestException}. - */ - public static class Builder extends BaseBuilder implements io.helidon.common.Builder { - private String errorCode; - private String errorMessage; - - private Builder() { - } - - @Override - public OciRestException build() { - return new OciRestException(this); - } - - /** - * OCI specific error code. - * - * @param errorCode OCI error code - * @return updated builder - */ - public Builder errorCode(String errorCode) { - this.errorCode = errorCode; - return this; - } - - /** - * OCI specific error message. - * - * @param errorMessage OCI error message - * @return updated builder - */ - public Builder errorMessage(String errorMessage) { - this.errorMessage = errorMessage; - return this; - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciSignatureData.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciSignatureData.java deleted file mode 100644 index 986fc91422a..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/OciSignatureData.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.security.interfaces.RSAPrivateKey; - -/** - * Data needed to sign requests. - */ -public interface OciSignatureData { - /** - * Key ID to use. - * - * @return key ID - */ - String keyId(); - - /** - * Private key to use to generate signatures. - * - * @return private key - */ - RSAPrivateKey privateKey(); - - /** - * Create a new instance. - * - * @param keyId key ID - * @param privateKey private key - * @return a new instance of signature data - */ - static OciSignatureData create(String keyId, RSAPrivateKey privateKey) { - return new OciSignatureData() { - @Override - public String keyId() { - return keyId; - } - - @Override - public RSAPrivateKey privateKey() { - return privateKey; - } - - @Override - public String toString() { - return keyId; - } - }; - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/RsaSessionKeys.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/RsaSessionKeys.java deleted file mode 100644 index 08fa5bc034b..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/RsaSessionKeys.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.atomic.AtomicReference; - -import io.helidon.common.LazyValue; - -class RsaSessionKeys implements SessionKeys { - private static final LazyValue KEY_PAIR_GENERATOR = LazyValue.create(() -> { - try { - KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); - generator.initialize(2048); - return generator; - } catch (NoSuchAlgorithmException e) { - throw new OciApiException("Failed to initialize KeyPairGenerator for RSA with 2048 key", e); - } - }); - - private final AtomicReference currentPair = new AtomicReference<>(); - - private RsaSessionKeys(KeyPair initialKeys) { - this.currentPair.set(initialKeys); - } - - static RsaSessionKeys create() { - return new RsaSessionKeys(KEY_PAIR_GENERATOR.get().generateKeyPair()); - } - - @Override - public KeyPair keyPair() { - return currentPair.get(); - } - - @Override - public KeyPair refresh() { - KeyPair keyPair = currentPair.get(); - - currentPair.compareAndSet(keyPair, KEY_PAIR_GENERATOR.get().generateKeyPair()); - - return currentPair.get(); - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/SessionKeys.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/SessionKeys.java deleted file mode 100644 index 66e2169ed32..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/SessionKeys.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect; - -import java.security.KeyPair; - -/** - * Provider of session keys when using Instance principal security. - */ -public interface SessionKeys { - /** - * Create default instance (using RSA). - * - * @return new session keys - */ - static SessionKeys create() { - return RsaSessionKeys.create(); - } - - /** - * Get the key pair. - * - * @return key pair - */ - KeyPair keyPair(); - - /** - * Refresh keys. - * - * @return a new key pair - */ - KeyPair refresh(); -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/package-info.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/package-info.java deleted file mode 100644 index ec29e69c948..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Classes needed for OCI to connect to service API. - */ -package io.helidon.integrations.oci.connect; diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/InjectionProvider.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/InjectionProvider.java deleted file mode 100644 index 99dd7ae1e6e..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/InjectionProvider.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.connect.spi; - -import java.util.List; - -import io.helidon.config.Config; -import io.helidon.integrations.oci.connect.OciRestApi; - -/** - * A Java Service Loader service for locating injectable instances. - */ -public interface InjectionProvider { - /** - * List of injectable types supported by this provider. - * - * @return list of types - */ - List> injectables(); - - /** - * An interface to allow lambda for creating instances. - * @param type of the instance to be created - */ - @FunctionalInterface - interface CreateInstanceFunction { - /** - * Create a new instance in singleton scope (or Application for CDI). - * - * @param restApi OCI rest API configured to the correct instance - * @param ociConfig configuration located on the oci node - * @return a new instance to be injected - */ - T apply(OciRestApi restApi, Config ociConfig); - } - - /** - * A single type that can be injected. - * - * @param type to inject - */ - class InjectionType { - private final Class type; - private final CreateInstanceFunction creator; - - private InjectionType(Class type, CreateInstanceFunction creator) { - this.type = type; - this.creator = creator; - } - - /** - * Create an injection type for a type and associated function to create an instance. - * - * @param type class that can be injected - * @param creator function to create a new instance - * @param type of the injectable - * @return a new injection type - */ - public static InjectionType create(Class type, CreateInstanceFunction creator) { - return new InjectionType<>(type, creator); - } - - /** - * Get the class of the injectable. - * - * @return class - */ - public Class injectedType() { - return type; - } - - /** - * Create a new instance of the injectable. - * - * @param ociRestApi rest API to use - * @param vaultConfig configuration of Vault (may be empty, never null) - * @return a new instance - */ - public T createInstance(OciRestApi ociRestApi, Config vaultConfig) { - return creator.apply(ociRestApi, vaultConfig); - } - } -} diff --git a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/package-info.java b/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/package-info.java deleted file mode 100644 index f5f508f9e1e..00000000000 --- a/integrations/oci/connect/src/main/java/io/helidon/integrations/oci/connect/spi/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Service provider interfaces for OCI connect. - */ -package io.helidon.integrations.oci.connect.spi; diff --git a/integrations/oci/connect/src/main/java/module-info.java b/integrations/oci/connect/src/main/java/module-info.java deleted file mode 100644 index 15868f12fe9..00000000000 --- a/integrations/oci/connect/src/main/java/module-info.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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. - */ - -/** - * Classes needed for OCI to connect to service API. - */ -module io.helidon.integrations.oci.connect { - requires java.logging; - - requires io.helidon.common; - requires io.helidon.common.crypto; - requires io.helidon.common.pki; - requires io.helidon.faulttolerance; - requires io.helidon.media.jsonp; - requires io.helidon.security; - requires io.helidon.security.providers.common; - requires io.helidon.security.providers.httpsign; - requires io.helidon.security.util; - requires io.helidon.webclient.security; - requires static io.helidon.config.metadata; - - requires transitive jakarta.json; - - requires transitive io.helidon.common.http; - requires transitive io.helidon.common.reactive; - requires transitive io.helidon.config; - requires transitive io.helidon.integrations.common.rest; - requires transitive io.helidon.webclient; - - exports io.helidon.integrations.oci.connect; - exports io.helidon.integrations.oci.connect.spi; - - opens io.helidon.integrations.oci.connect to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/objectstorage-health/pom.xml b/integrations/oci/objectstorage-health/pom.xml deleted file mode 100644 index 05daf3b3397..00000000000 --- a/integrations/oci/objectstorage-health/pom.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-objectstorage-health - Helidon Integrations OCI Object Storage Health Check - - OCI Object Storage Health Check Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage - - - io.helidon.common - helidon-common - - - io.helidon.config - helidon-config - - - io.helidon.health - helidon-health - - - io.helidon.health - helidon-health-common - - - io.helidon.config - helidon-config-yaml - test - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - true - - - - org.eclipse.microprofile.config - microprofile-config-api - provided - true - - - org.junit.jupiter - junit-jupiter-api - test - - - org.hamcrest - hamcrest-all - test - - - diff --git a/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheck.java b/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheck.java deleted file mode 100644 index 756b8ac2d5e..00000000000 --- a/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheck.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage.health; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.logging.Logger; - -import io.helidon.config.Config; -import io.helidon.health.common.BuiltInHealthCheck; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.objectstorage.GetBucket; -import io.helidon.integrations.oci.objectstorage.OciObjectStorage; -import io.helidon.integrations.oci.objectstorage.OciObjectStorageRx; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.health.HealthCheck; -import org.eclipse.microprofile.health.HealthCheckResponse; -import org.eclipse.microprofile.health.HealthCheckResponseBuilder; -import org.eclipse.microprofile.health.Liveness; - -import static io.helidon.common.http.Http.Status.OK_200; - -/** - * Liveness check for an OCI's ObjectStorage bucket. Reads configuration to - * obtain bucket name as well as OCI properties from '~/.oci/config'. - */ -@Liveness -@ApplicationScoped // this will be ignored if not within CDI -@BuiltInHealthCheck -public final class OciObjectStorageHealthCheck implements HealthCheck { - private static final Logger LOGGER = Logger.getLogger(OciObjectStorageHealthCheck.class.getName()); - - private final List buckets; - private final String namespace; - private final OciObjectStorage ociObjectStorage; - - @Inject - OciObjectStorageHealthCheck(@ConfigProperty(name = "oci.objectstorage.namespace") String namespace, - @ConfigProperty(name = "oci.objectstorage.healthchecks") List buckets, - OciObjectStorage ociObjectStorage) { - this.buckets = buckets; - this.namespace = namespace; - this.ociObjectStorage = ociObjectStorage; - } - - private OciObjectStorageHealthCheck(Builder builder) { - this.buckets = builder.buckets; - this.namespace = builder.namespace; - this.ociObjectStorage = builder.ociObjectStorage; - } - - /** - * Create a new fluent API builder to configure a new health check. - * - * @return builder instance - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create an instance. - * - * @param config the config. - * @return an instance. - */ - public static OciObjectStorageHealthCheck create(Config config) { - return builder().config(config).build(); - } - - /** - * Checks that the OCI Object Storage buckets are accessible. Will report a status code - * or an error message for each bucket as data. Can block since all health checks are - * called asynchronously. - * - * @return a response - */ - @Override - public HealthCheckResponse call() { - HealthCheckResponseBuilder builder = HealthCheckResponse.named("objectStorage"); - - boolean status = true; - for (String bucket : buckets) { - try { - ApiOptionalResponse r = ociObjectStorage.getBucket(GetBucket.Request.builder() - .namespace(namespace) - .bucket(bucket)); - LOGGER.fine(() -> "OCI ObjectStorage health check for bucket " + bucket - + " returned status code " + r.status().code()); - builder.withData(bucket, r.status().code()); - status = status && r.status().equals(OK_200); - } catch (Throwable t) { - LOGGER.fine(() -> "OCI ObjectStorage health check for bucket " + bucket - + " exception " + t.getMessage()); - status = false; - builder.withData(bucket, t.getMessage()); - } - } - - builder.status(status); - return builder.build(); - } - - /** - * Fluent API builder for {@link OciObjectStorageHealthCheck}. - */ - public static final class Builder implements io.helidon.common.Builder { - - private String namespace; - private OciObjectStorageRx ociObjectStorageRx; - private OciObjectStorage ociObjectStorage; - private final List buckets = new ArrayList<>(); - - private Builder() { - } - - @Override - public OciObjectStorageHealthCheck build() { - Objects.requireNonNull(namespace); - if (ociObjectStorageRx == null) { - ociObjectStorageRx = OciObjectStorageRx.builder() - .namespace(namespace) - .build(); - } - this.ociObjectStorage = OciObjectStorage.create(ociObjectStorageRx); - return new OciObjectStorageHealthCheck(this); - } - - /** - * Set up this builder using config. - * - * @param config the config. - * @return the builder. - */ - public Builder config(Config config) { - config.get("oci.objectstorage.namespace").asString().ifPresent(this::namespace); - config.get("oci.objectstorage.healthchecks").asList(String.class).ifPresent(this.buckets::addAll); - return this; - } - - /** - * Set the underlying OCI ObjectStorage RX client. - * - * @param ociObjectStorage object storage RX client. - * @return the builder. - */ - public Builder ociObjectStorage(OciObjectStorageRx ociObjectStorage) { - this.ociObjectStorageRx = ociObjectStorage; - return this; - } - - /** - * Add a bucket to the list. - * - * @param bucket bucket's name. - * @return the builder. - */ - public Builder addBucket(String bucket) { - this.buckets.add(bucket); - return this; - } - - /** - * Set the namespace. - * - * @param namespace the namespace. - * @return the builder. - */ - public Builder namespace(String namespace) { - this.namespace = namespace; - return this; - } - } -} diff --git a/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/package-info.java b/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/package-info.java deleted file mode 100644 index c90af45f32c..00000000000 --- a/integrations/oci/objectstorage-health/src/main/java/io/helidon/integrations/oci/objectstorage/health/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Object Storage Health. - */ -package io.helidon.integrations.oci.objectstorage.health; diff --git a/integrations/oci/objectstorage-health/src/main/java/module-info.java b/integrations/oci/objectstorage-health/src/main/java/module-info.java deleted file mode 100644 index f14a77de32e..00000000000 --- a/integrations/oci/objectstorage-health/src/main/java/module-info.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Object Storage Health. - */ -module io.helidon.integrations.oci.objectstorage.health { - requires transitive jakarta.json; - requires transitive io.helidon.common.reactive; - requires transitive io.helidon.integrations.oci.connect; - requires transitive io.helidon.config; - - requires io.helidon.integrations.common.rest; - requires io.helidon.common.http; - requires io.helidon.health; - requires io.helidon.health.common; - requires io.helidon.integrations.oci.objectstorage; - - requires static microprofile.config.api; - requires static jakarta.cdi; - requires static jakarta.inject; - requires java.logging; - - exports io.helidon.integrations.oci.objectstorage.health; - - opens io.helidon.integrations.oci.objectstorage.health to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/objectstorage-health/src/main/resources/META-INF/beans.xml b/integrations/oci/objectstorage-health/src/main/resources/META-INF/beans.xml deleted file mode 100644 index dbf3e648c1e..00000000000 --- a/integrations/oci/objectstorage-health/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/integrations/oci/objectstorage-health/src/test/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheckTest.java b/integrations/oci/objectstorage-health/src/test/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheckTest.java deleted file mode 100644 index 429b08f2bf5..00000000000 --- a/integrations/oci/objectstorage-health/src/test/java/io/helidon/integrations/oci/objectstorage/health/OciObjectStorageHealthCheckTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage.health; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -class OciObjectStorageHealthCheckTest { - - @Test - @Disabled // Cannot run for now without private key - void testHealthCheck() { - OciObjectStorageHealthCheck healthCheck = OciObjectStorageHealthCheck.builder().build(); - - } -} diff --git a/integrations/oci/objectstorage-health/src/test/resources/application.yaml b/integrations/oci/objectstorage-health/src/test/resources/application.yaml deleted file mode 100644 index e3c2886f7df..00000000000 --- a/integrations/oci/objectstorage-health/src/test/resources/application.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# -# 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. -# - -server: - port: 8080 - -oci: - objectstorage: - namespace: "namespace" - bucket: "main" - diff --git a/integrations/oci/objectstorage/pom.xml b/integrations/oci/objectstorage/pom.xml deleted file mode 100644 index 798f446e29b..00000000000 --- a/integrations/oci/objectstorage/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-objectstorage - Helidon Integrations OCI Object Storage - - OCI Object Storage Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - - - io.helidon.common - helidon-common - - - io.helidon.config - helidon-config - - - io.helidon.config - helidon-config-yaml - test - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - true - - - - org.eclipse.microprofile.config - microprofile-config-api - provided - true - - - org.junit.jupiter - junit-jupiter-api - test - - - org.hamcrest - hamcrest-all - test - - - \ No newline at end of file diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/DeleteObject.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/DeleteObject.java deleted file mode 100644 index 801f6d6ffdb..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/DeleteObject.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.Optional; - -import io.helidon.integrations.common.rest.ApiResponse; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Delete Object request and response. - */ -public final class DeleteObject { - private DeleteObject() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends ObjectRequest { - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - } - - /** - * Response object for responses without an entity. - */ - public static final class Response extends ApiResponse { - private Response(Builder builder) { - super(builder); - } - - static Builder builder() { - return new Builder(); - } - - static final class Builder extends ApiResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetBucket.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetBucket.java deleted file mode 100644 index 376dd437741..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetBucket.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.time.Instant; -import java.util.Optional; - -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Get Bucket request and response. - */ -public final class GetBucket { - private GetBucket() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private String namespace; - private String bucket; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - - /** - * The Object Storage namespace used for the request. - * Override the default namespace (if one is configured). - * - * @param namespace namespace - * @return updated request - */ - public GetBucket.Request namespace(String namespace) { - this.namespace = namespace; - return this; - } - - /** - * The name of the bucket. Avoid entering confidential information. - * Required. - * - * @param bucket bucket name - * @return updated requst - */ - public GetBucket.Request bucket(String bucket) { - this.bucket = bucket; - return this; - } - - /** - * Namespace if configured on this request. - * - * @return namespace or empty if not configured per request - */ - public Optional namespace() { - return Optional.ofNullable(namespace); - } - - /** - * Name of the bucket, required. - * - * @return bucket name - */ - public String bucket() { - if (bucket == null) { - throw new OciApiException("Bucket name must be defined for PutObject request."); - } - return bucket; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final String bucketId; - private final String namespace; - private final Instant created; - private final String name; - private final String compartmentId; - private final String createdBy; - private final int approximateCount; - private final long approximateSize; - - private Response(JsonObject json) { - this.bucketId = json.getString("id"); - this.namespace = json.getString("namespace"); - this.created = getInstant(json, "timeCreated"); - this.name = json.getString("name"); - this.compartmentId = json.getString("compartmentId"); - this.createdBy = json.getString("createdBy"); - this.approximateCount = json.getInt("approximateCount", -1); - - if (json.containsKey("approximateSize") && !json.isNull("approximateSize")) { - this.approximateSize = json.getJsonNumber("approximateSize").longValue(); - } else { - this.approximateSize = -1; - } - } - - static Response create(JsonObject json) { - return new Response(json); - } - - /** - * Bucket OCID. - * - * @return bucket id - */ - public String bucketId() { - return bucketId; - } - - /** - * The Object Storage namespace in which the bucket resides. - * - * @return namespace - */ - public String namespace() { - return namespace; - } - - /** - * The date and time the bucket was created. - * - * @return created instant - */ - public Instant created() { - return created; - } - - /** - * The name of the bucket. Avoid entering confidential information. Example: my-new-bucket1. - * - * @return bucket name - */ - public String name() { - return name; - } - - /** - * The compartment ID in which the bucket is authorized. - * - * @return compartment ID - */ - public String compartmentId() { - return compartmentId; - } - - /** - * The OCID of the user who created the bucket. - * @return user OCID - */ - public String createdBy() { - return createdBy; - } - - /** - * The approximate number of objects in the bucket. Count statistics are reported periodically. You will see a lag - * between what is displayed and the actual object count. - * - * @return count on {@code -1} if not available - */ - public int approximateCount() { - return approximateCount; - } - - /** - * The approximate total size in bytes of all objects in the bucket. Size statistics are reported periodically. You - * will see a lag between what is displayed and the actual size of the bucket. - * - * @return size or {@code -1} if not available - */ - public long approximateSize() { - return approximateSize; - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObject.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObject.java deleted file mode 100644 index 1c898353fee..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObject.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; -import java.util.Optional; - -import io.helidon.common.reactive.IoMulti; -import io.helidon.common.reactive.Multi; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Get Object request and response. - */ -public final class GetObject { - private GetObject() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends ObjectRequest { - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - } - - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final Multi publisher; - - private Response(Multi publisher) { - this.publisher = publisher; - } - - static Response create(Multi publisher) { - return new Response(publisher); - } - - /** - * Write the response to the provided byte channel. - * - * @param channel channel to write to - * @see java.nio.channels.Channels#newChannel(java.io.OutputStream) - */ - public void writeTo(WritableByteChannel channel) { - publisher.to(IoMulti.multiToByteChannel(channel)) - .await(); - } - - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObjectRx.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObjectRx.java deleted file mode 100644 index 86ed057d5f8..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/GetObjectRx.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.nio.ByteBuffer; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.reactive.Multi; -import io.helidon.integrations.oci.connect.OciResponseParser; - -/** - * Reactive get object request and response. - */ -public final class GetObjectRx { - private GetObjectRx() { - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final Multi publisher; - - private Response(Multi publisher) { - this.publisher = publisher; - } - - static Response create(Multi publisher) { - return new Response(publisher); - } - - /** - * Get a publisher of {@link io.helidon.common.http.DataChunk}, this is useful - * when the result is sent via WebServer or WebClient that also use it. - * - * @return publisher of data chunks - */ - public Multi publisher() { - return publisher; - } - - /** - * Get a publisher of byte buffers. - * - * @return publisher of byte buffers - */ - public Multi bytePublisher() { - return publisher.flatMap(it -> Multi.just(it.data())); - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/ObjectRequest.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/ObjectRequest.java deleted file mode 100644 index 13b45a0c2ff..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/ObjectRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.Optional; - -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -/** - * Object store base request class. - * - * @param type of the subclass - */ -public abstract class ObjectRequest> extends OciRequestBase { - private String namespace; - private String bucket; - private String objectName; - - /** - * The Object Storage namespace used for the request. - * Override the default namespace (if one is configured). - * - * @param namespace namespace - * @return updated request - */ - public T namespace(String namespace) { - this.namespace = namespace; - return me(); - } - - /** - * The name of the bucket. Avoid entering confidential information. - * Required. - * - * @param bucket bucket name - * @return updated requst - */ - public T bucket(String bucket) { - this.bucket = bucket; - return me(); - } - - /** - * The name of the object. Avoid entering confidential information. - * Required. - * - * @param objectName name of the object - * @return updated request - */ - public T objectName(String objectName) { - this.objectName = objectName; - return me(); - } - - /** - * Namespace if configured on this request. - * - * @return namespace or empty if not configured per request - */ - public Optional namespace() { - return Optional.ofNullable(namespace); - } - - /** - * Name of the bucket, required. - * - * @return bucket name - */ - public String bucket() { - if (bucket == null) { - throw new OciApiException("Bucket name must be defined for PutObject request."); - } - return bucket; - } - - /** - * Object name, required. - * - * @return object name - */ - public String objectName() { - if (objectName == null) { - throw new OciApiException("Object name must be defined for PutObject request."); - } - return objectName; - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorage.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorage.java deleted file mode 100644 index 0f0d3a6fe77..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorage.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.nio.channels.ReadableByteChannel; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -/** - * Blocking OCI ObjectStorage API. - * All methods block the current thread. This implementation is not suitable for reactive programming. - * Use {@link io.helidon.integrations.oci.objectstorage.OciObjectStorageRx} in reactive code. - */ -public interface OciObjectStorage { - /** - * Create a blocking object storage integration from its reactive counterpart. - * When running within an injection capable environment (such as CDI), instances of this - * class can be injected. - * - * @param reactive reactive OCI object storage - * @return blocking OCI object storage - */ - static OciObjectStorage create(OciObjectStorageRx reactive) { - return new OciObjectStorageImpl(reactive); - } - - /** - * Gets the metadata and body of an object. - * - * @param request get object request - * @return future with response or error - */ - ApiOptionalResponse getObject(GetObject.Request request); - - /** - * Creates a new object or overwrites an existing object with the same name. The maximum object size allowed by PutObject - * is 50 GiB. - * - * @param request put object request - * @param channel to read data from - * @return future with response or error - */ - PutObject.Response putObject(PutObject.Request request, ReadableByteChannel channel); - - /** - * Deletes an object. - * @param request delete object request - * @return future with response or error - */ - DeleteObject.Response deleteObject(DeleteObject.Request request); - - /** - * Rename an object in the given Object Storage namespace. - * See Object Names. - * - * @param request rename object request - * @return future with response or error - */ - RenameObject.Response renameObject(RenameObject.Request request); - - /** - * Gets the metadata of a bucket. - * - * @param request get bucket request - * @return future with response or error - */ - ApiOptionalResponse getBucket(GetBucket.Request request); -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageImpl.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageImpl.java deleted file mode 100644 index 0bd4863a25c..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.nio.channels.ReadableByteChannel; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.reactive.IoMulti; -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -class OciObjectStorageImpl implements OciObjectStorage { - private final OciObjectStorageRx delegate; - - OciObjectStorageImpl(OciObjectStorageRx delegate) { - this.delegate = delegate; - } - - @Override - public ApiOptionalResponse getObject(GetObject.Request request) { - return delegate.getObject(request).await() - .map(reactiveResponse -> GetObject.Response.create(reactiveResponse.bytePublisher())); - } - - @Override - public PutObject.Response putObject(PutObject.Request request, ReadableByteChannel channel) { - return delegate.putObject(request, IoMulti.multiFromByteChannel(channel).map(DataChunk::create)) - .await(); - } - - @Override - public DeleteObject.Response deleteObject(DeleteObject.Request request) { - return delegate.deleteObject(request) - .await(); - } - - @Override - public RenameObject.Response renameObject(RenameObject.Request request) { - return delegate.renameObject(request) - .await(); - } - - @Override - public ApiOptionalResponse getBucket(GetBucket.Request request) { - return delegate.getBucket(request) - .await(); - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageInjectionProvider.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageInjectionProvider.java deleted file mode 100644 index a93349c3e42..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageInjectionProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.LinkedList; -import java.util.List; - -import io.helidon.integrations.oci.connect.spi.InjectionProvider; - -/** - * Service provider for {@link io.helidon.integrations.oci.connect.spi.InjectionProvider}. - * Only use by service loader. - * @deprecated do not use directly - */ -@Deprecated -public class OciObjectStorageInjectionProvider implements InjectionProvider { - private static final List> INJECTABLES; - - static { - List> injectables = new LinkedList<>(); - - injectables.add(InjectionType.create(OciObjectStorageRx.class, - (restApi, config) -> OciObjectStorageRx.builder() - .restApi(restApi) - .config(config) - .build())); - - injectables.add(InjectionType.create(OciObjectStorage.class, - (restApi, config) -> OciObjectStorage.create(OciObjectStorageRx.builder() - .restApi(restApi) - .config(config) - .build()))); - - INJECTABLES = List.copyOf(injectables); - } - - /** - * This constructor is only intended for service loader. - * DO NOT USE DIRECTLY. - * @deprecated do not use - */ - @Deprecated - public OciObjectStorageInjectionProvider() { - } - - @Override - public List> injectables() { - return INJECTABLES; - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRx.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRx.java deleted file mode 100644 index 9d1b6ddbfb4..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRx.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.Optional; -import java.util.concurrent.Flow; -import java.util.function.Consumer; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciRestApi; - -/** - * Reactive API for OCI Object Storage. - */ -public interface OciObjectStorageRx { - /** - * Version of Secret API supported by this client. - */ - String API_VERSION = "20160918"; - - /** - * Host name prefix. - */ - String API_HOST_PREFIX = "objectstorage"; - - /** - * Host format of API server. - */ - String API_HOST_FORMAT = "%s://%s.%s.%s"; - - /** - * Create a new fluent API builder for OCI object storage. - * - * @return a new builder - */ - static Builder builder() { - return new Builder(); - } - - /** - * Create OCI Object Storage using the default {@link io.helidon.integrations.oci.connect.OciRestApi}. - * - * @return OCI object storage instance connecting based on {@code DEFAULT} profile - */ - static OciObjectStorageRx create() { - return builder().build(); - } - - /** - * Create OCI Object Storage based on configuration. - * - * @param config configuration on the node of OCI configuration - * @return OCI object storage instance configured from the configuration - * @see OciObjectStorageRx.Builder#config(io.helidon.config.Config) - */ - static OciObjectStorageRx create(Config config) { - return builder().config(config).build(); - } - - /** - * Gets the metadata and body of an object. - * - * @param request get object request - * @return future with response or error - */ - Single> getObject(GetObject.Request request); - - /** - * Creates a new object or overwrites an existing object with the same name. The maximum object size allowed by PutObject - * is 50 GiB. - * - * @param request put object request - * @param publisher publisher of object's data - * @return future with response or error - */ - Single putObject(PutObject.Request request, Flow.Publisher publisher); - - /** - * Deletes an object. - * @param request delete object request - * @return future with response or error - */ - Single deleteObject(DeleteObject.Request request); - - /** - * Rename an object in the given Object Storage namespace. - * See Object Names. - * - * @param request rename object request - * @return future with response or error - */ - Single renameObject(RenameObject.Request request); - - /** - * Gets the metadata of a bucket. - * - * @param request get bucket request - * @return future with response or error - */ - Single> getBucket(GetBucket.Request request); - - /** - * Fluent API Builder for {@link io.helidon.integrations.oci.objectstorage.OciObjectStorageRx}. - */ - class Builder implements io.helidon.common.Builder { - private final OciRestApi.Builder apiBuilder = OciRestApi.builder(); - - private String hostPrefix = API_HOST_PREFIX; - private String namespace; - private String endpoint; - private OciRestApi restApi; - - private Builder() { - } - - @Override - public OciObjectStorageRx build() { - if (restApi == null) { - restApi = apiBuilder.build(); - } - return new OciObjectStorageRxImpl(this); - } - - /** - * Update from configuration. The configuration must be located on the {@code OCI} root configuration - * node. - * - * @param config configuration - * @return updated builder - */ - public Builder config(Config config) { - apiBuilder.config(config); - config.get("objectstorage.host-prefix").asString().ifPresent(this::hostPrefix); - config.get("objectstorage.endpoint").asString().ifPresent(this::endpoint); - config.get("objectstorage.namespace").asString().ifPresent(this::namespace); - return this; - } - - /** - * Instance of rest API to use. - * - * @param restApi rest API - * @return updated builder - */ - public Builder restApi(OciRestApi restApi) { - this.restApi = restApi; - return this; - } - - /** - * Host prefix to use for object storage, - * defaults to {@value API_HOST_PREFIX}. - * - * @param prefix prefix to use - * @return updated builder - */ - public Builder hostPrefix(String prefix) { - this.hostPrefix = prefix; - return this; - } - - /** - * Explicit endpoint to use. - * - * @param endpoint endpoint - * @return updated builder - */ - public Builder endpoint(String endpoint) { - this.endpoint = endpoint; - return this; - } - - /** - * Object storage namespace to use. - * - * @param namespace object storage namespace - * @return updated builder - */ - public Builder namespace(String namespace) { - this.namespace = namespace; - return this; - } - - /** - * Update the rest access builder to modify defaults. - * - * @param builderConsumer consumer of the builder - * @return updated builder - */ - public Builder updateRestApi(Consumer builderConsumer) { - builderConsumer.accept(apiBuilder); - return this; - } - - OciRestApi restApi() { - return restApi; - } - - Optional namespace() { - return Optional.ofNullable(namespace); - } - - String hostPrefix() { - return hostPrefix; - } - - String endpoint() { - return endpoint; - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRxImpl.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRxImpl.java deleted file mode 100644 index a4b13df62f0..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/OciObjectStorageRxImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.Optional; -import java.util.concurrent.Flow; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; -import io.helidon.common.reactive.Single; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciRestApi; - -import jakarta.json.JsonObject; - -class OciObjectStorageRxImpl implements OciObjectStorageRx { - private final OciRestApi restApi; - private final Optional defaultNamespace; - private final String hostPrefix; - private final Optional endpoint; - - OciObjectStorageRxImpl(Builder builder) { - this.restApi = builder.restApi(); - this.defaultNamespace = builder.namespace(); - this.hostPrefix = builder.hostPrefix(); - this.endpoint = Optional.ofNullable(builder.endpoint()); - } - - @Override - public Single> getObject(GetObject.Request request) { - String namespace = namespace(request); - String apiPath = "/n/" + namespace + "/b/" + request.bucket() + "/o/" + request.objectName(); - - objectStorage(request); - - return restApi - .getPublisher(apiPath, request, ApiOptionalResponse., GetObjectRx.Response>apiResponseBuilder() - .entityProcessor(GetObjectRx.Response::create)); - } - - @Override - public Single putObject(PutObject.Request request, Flow.Publisher publisher) { - String namespace = namespace(request); - String apiPath = "/n/" + namespace + "/b/" + request.bucket() + "/o/" + request.objectName(); - - request.addHeader("Content-Length", String.valueOf(request.contentLength())); - objectStorage(request); - - return restApi.invokeBytesRequest(Http.Method.PUT, apiPath, request, publisher, PutObject.Response.builder()); - } - - @Override - public Single deleteObject(DeleteObject.Request request) { - String namespace = namespace(request); - String apiPath = "/n/" + namespace + "/b/" + request.bucket() + "/o/" + request.objectName(); - - objectStorage(request); - - return restApi.delete(apiPath, request, DeleteObject.Response.builder()); - } - - @Override - public Single renameObject(RenameObject.Request request) { - String namespace = namespace(request); - String apiPath = "/n/" + namespace + "/b/" + request.bucket() + "/actions/renameObject"; - - objectStorage(request); - - return restApi.post(apiPath, request, RenameObject.Response.builder()); - } - - @Override - public Single> getBucket(GetBucket.Request request) { - String namespace = request.namespace() - .or(() -> defaultNamespace) - .orElseThrow(() -> new OciApiException("Namespace must be defined for Object Storage requests either " - + "in configuration of Object Storage, or on request.")); - - String apiPath = "/n/" + namespace + "/b/" + request.bucket(); - - objectStorage(request); - - return restApi.get(apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(GetBucket.Response::create)); - } - - private String namespace(ObjectRequest request) { - return request.namespace() - .or(() -> defaultNamespace) - .orElseThrow(() -> new OciApiException("Namespace must be defined for Object Storage requests either " - + "in configuration of Object Storage, or on request.")); - } - - private void objectStorage(OciRequestBase request) { - if (request.endpoint().isPresent()) { - return; - } - - endpoint.ifPresent(request::endpoint); - - request.hostFormat(OciObjectStorageRx.API_HOST_FORMAT) - .hostPrefix(hostPrefix); - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/PutObject.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/PutObject.java deleted file mode 100644 index 3a97a3c34d1..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/PutObject.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import java.util.Optional; - -import io.helidon.integrations.common.rest.ApiResponse; -import io.helidon.integrations.oci.connect.OciApiException; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Put object request and response. - */ -public final class PutObject { - private PutObject() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends ObjectRequest { - private Long contentLength; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The content length of the body (number of bytes in the request entity). - * Required. - * - * @param contentLength content length - * @return updated request - */ - public Request contentLength(long contentLength) { - this.contentLength = contentLength; - return this; - } - - /** - * Content lenght configured on this request. - * - * @return content lenght, must be preset - */ - public long contentLength() { - if (contentLength == null) { - throw new OciApiException("Content-Length must be defined for PutObject request."); - } - return contentLength; - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - } - - /** - * Response object for responses without an entity. - */ - public static final class Response extends ApiResponse { - private Response(Builder builder) { - super(builder); - } - - static Builder builder() { - return new Builder(); - } - - static final class Builder extends ApiResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/RenameObject.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/RenameObject.java deleted file mode 100644 index bf0ad361476..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/RenameObject.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.objectstorage; - -import io.helidon.integrations.common.rest.ApiResponse; - -/** - * Objects related to RenameObject API calls. - */ -public final class RenameObject { - private RenameObject() { - } - - /** - * Rename Object request. - */ - public static class Request extends ObjectRequest { - private Request() { - } - - /** - * Create a new request builder. - * - * @return a new request - */ - public static Request builder() { - return new Request(); - } - - /** - * The name of the source object to be renamed. - * - * @param objectName name of the object - * @return updated request - */ - @Override - public Request objectName(String objectName) { - return add("sourceName", objectName); - } - - /** - * The new name of the source object. Avoid entering confidential information. - * Required. - * - * @param objectName new name of the object - * @return updated request - */ - public Request newObjectName(String objectName) { - return add("newName", objectName); - } - - /** - * The if-match entity tag (ETag) of the new object. - * - * @param eTag entity tag for the new object - * @return updated request - */ - public Request newIfMatchETag(String eTag) { - return add("newObjIfMatchETag", eTag); - } - - /** - * The if-none-match entity tag (ETag) of the new object. - * - * @param eTag entity tag - * @return updated request - */ - public Request newIfNoneMatchETag(String eTag) { - return add("newObjIfNoneMatchETag", eTag); - } - - /** - * The if-match entity tag (ETag) of the source object. - * - * @param eTag entity tag - * @return updated request - */ - public Request oldIfMatchETag(String eTag) { - return add("srcObjIfMatchETag", eTag); - } - } - - /** - * Response object for responses without an entity. - */ - public static final class Response extends ApiResponse { - private Response(Builder builder) { - super(builder); - } - - static Builder builder() { - return new Builder(); - } - - static final class Builder extends ApiResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/package-info.java b/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/package-info.java deleted file mode 100644 index 796d78e78e9..00000000000 --- a/integrations/oci/objectstorage/src/main/java/io/helidon/integrations/oci/objectstorage/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Object Storage integration. - * - * @see io.helidon.integrations.oci.objectstorage.OciObjectStorage - * @see io.helidon.integrations.oci.objectstorage.OciObjectStorageRx - */ -package io.helidon.integrations.oci.objectstorage; diff --git a/integrations/oci/objectstorage/src/main/java/module-info.java b/integrations/oci/objectstorage/src/main/java/module-info.java deleted file mode 100644 index 35fb6d5c5fb..00000000000 --- a/integrations/oci/objectstorage/src/main/java/module-info.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Object Storage integration. - * - * @see io.helidon.integrations.oci.objectstorage.OciObjectStorage - * @see io.helidon.integrations.oci.objectstorage.OciObjectStorageRx - */ -module io.helidon.integrations.oci.objectstorage { - requires transitive jakarta.json; - requires transitive io.helidon.common.reactive; - requires transitive io.helidon.integrations.oci.connect; - requires transitive io.helidon.config; - - requires io.helidon.integrations.common.rest; - requires io.helidon.common.http; - - exports io.helidon.integrations.oci.objectstorage; - - // this is the intended usage, deprecation is to warn about accidental usage in code - //noinspection deprecation - provides io.helidon.integrations.oci.connect.spi.InjectionProvider - with io.helidon.integrations.oci.objectstorage.OciObjectStorageInjectionProvider; - - opens io.helidon.integrations.oci.objectstorage to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/objectstorage/src/main/resources/META-INF/helidon/native-image/weld-proxies.json b/integrations/oci/objectstorage/src/main/resources/META-INF/helidon/native-image/weld-proxies.json deleted file mode 100644 index 353a1716aba..00000000000 --- a/integrations/oci/objectstorage/src/main/resources/META-INF/helidon/native-image/weld-proxies.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.objectstorage.OciObjectStorage" - ] - }, - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.objectstorage.OciObjectStorageRx" - ] - } -] \ No newline at end of file diff --git a/integrations/oci/pom.xml b/integrations/oci/pom.xml index 3f0b5eb55ff..2a15786f9fc 100644 --- a/integrations/oci/pom.xml +++ b/integrations/oci/pom.xml @@ -33,14 +33,6 @@ pom - connect - telemetry - objectstorage - objectstorage-health - vault - vault-health - cdi - atp sdk diff --git a/integrations/oci/telemetry/pom.xml b/integrations/oci/telemetry/pom.xml deleted file mode 100644 index 18149731a5f..00000000000 --- a/integrations/oci/telemetry/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-telemetry - Helidon Integrations OCI Telemetry - - OCI Telemetry Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - - - io.helidon.common - helidon-common - - - io.helidon.common - helidon-common-http - - - io.helidon.config - helidon-config - - - \ No newline at end of file diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetrics.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetrics.java deleted file mode 100644 index d576e8423b6..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetrics.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -/** - * Blocking APIs for OCI Metrics. - */ -public interface OciMetrics { - /** - * Create blocking OCI metrics from its reactive counterpart. - * - * @param reactive reactive OCI metrics - * @return blocking OCI metrics - */ - static OciMetrics create(OciMetricsRx reactive) { - return new OciMetricsImpl(reactive); - } - - /** - * Publishes raw metric data points to the Monitoring service. For more information about publishing metrics, see - * Publishing Custom Metrics. - * For important limits information, see - * Limits on Monitoring. - * - * Per-call limits information follows. - * - * Dimensions per metric group*. Maximum: 20. Minimum: 1. - * Unique metric streams*. Maximum: 50. - * Transactions Per Second (TPS) per-tenancy limit for this operation: 50. - * *A metric group is the combination of a given metric, metric namespace, and tenancy for the purpose of determining - * limits. A dimension is a qualifier provided in a metric definition. A metric stream is an individual set of aggregated - * data for a metric, typically specific to a resource. For more information about metric-related concepts, see - * Monitoring Concepts. - * - * @param request metric request - * @return metric response - */ - PostMetricData.Response postMetricData(PostMetricData.Request request); -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsImpl.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsImpl.java deleted file mode 100644 index f93ecdc7789..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsImpl.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -class OciMetricsImpl implements OciMetrics { - private final OciMetricsRx delegate; - - OciMetricsImpl(OciMetricsRx delegate) { - this.delegate = delegate; - } - - @Override - public PostMetricData.Response postMetricData(PostMetricData.Request request) { - return delegate.postMetricData(request).await(); - } -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRx.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRx.java deleted file mode 100644 index 59d08736623..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRx.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -import java.util.function.Consumer; - -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.integrations.oci.connect.OciRestApi; - -/** - * Reactive APIs for OCI Metrics. - */ -public interface OciMetricsRx { - /** - * Version of API supported by this client. - */ - String API_VERSION = "20180401"; - /** - * Host name prefix. - */ - String API_HOST_PREFIX = "telemetry-ingestion"; - /** - * Host format of API server. - */ - String API_HOST_FORMAT = "%s://%s.%s.%s"; - - /** - * Create a new fluent API builder for OCI metrics. - * - * @return a new builder - */ - static Builder builder() { - return new Builder(); - } - - /** - * Create OCI metrics using the default {@link io.helidon.integrations.oci.connect.OciRestApi}. - * - * @return OCI metrics instance connecting based on {@code DEFAULT} profile - */ - static OciMetricsRx create() { - return builder().build(); - } - - /** - * Create OCI metrics based on configuration. - * - * @param config configuration on the node of OCI configuration - * @return OCI metrics instance configured from the configuration - * @see OciMetricsRx.Builder#config(io.helidon.config.Config) - */ - static OciMetricsRx create(Config config) { - return builder().config(config).build(); - } - - /** - * Publishes raw metric data points to the Monitoring service. For more information about publishing metrics, see - * Publishing Custom Metrics. - * For important limits information, see - * Limits on Monitoring. - * - * Per-call limits information follows. - * - * Dimensions per metric group*. Maximum: 20. Minimum: 1. - * Unique metric streams*. Maximum: 50. - * Transactions Per Second (TPS) per-tenancy limit for this operation: 50. - * *A metric group is the combination of a given metric, metric namespace, and tenancy for the purpose of determining - * limits. A dimension is a qualifier provided in a metric definition. A metric stream is an individual set of aggregated - * data for a metric, typically specific to a resource. For more information about metric-related concepts, see - * Monitoring Concepts. - * - * @param request metric request - * @return future metric response - */ - Single postMetricData(PostMetricData.Request request); - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.telemetry.OciMetricsRx}. - */ - class Builder implements io.helidon.common.Builder { - private final OciRestApi.Builder apiBuilder = OciRestApi.builder(); - - private String apiVersion = API_VERSION; - private String hostPrefix = API_HOST_PREFIX; - private String endpoint; - private OciRestApi restApi; - - private Builder() { - } - - @Override - public OciMetricsRx build() { - if (restApi == null) { - restApi = apiBuilder.build(); - } - - return new OciMetricsRxImpl(this); - } - - /** - * Update from configuration. The configuration must be located on the {@code OCI} root configuration - * node. - * - * @param config configuration - * @return updated metrics builder - */ - public Builder config(Config config) { - apiBuilder.config(config); - config.get("metrics.host-prefix").asString().ifPresent(this::hostPrefix); - config.get("metrics.endpoint").asString().ifPresent(this::endpoint); - config.get("metrics.api-version").asString().ifPresent(this::apiVersion); - return this; - } - - /** - * Replace host prefix. - * Changing host prefix may be a breaking change. This API is designed to work with - * {@link #API_HOST_PREFIX}. - * - * @param prefix prefix to use - * @return updated builder - */ - public Builder hostPrefix(String prefix) { - this.hostPrefix = prefix; - return this; - } - - /** - * Replace endpoint to be invoked when contacting OCI. - * - * @param endpoint endpoint to use - * @return updated builder - */ - public Builder endpoint(String endpoint) { - this.endpoint = endpoint; - return this; - } - - /** - * Configure API version to use. - * API version is part of the request URI. Changing API version is potentially breaking, - * as this API is designed to work with a specific version (see {@link #API_VERSION}). - * - * @param apiVersion version of the API to use - * @return updated builder - */ - public Builder apiVersion(String apiVersion) { - this.apiVersion = apiVersion; - return this; - } - - /** - * Update the rest API builder to modify defaults. - * - * @param builderConsumer consumer of the builder - * @return updated metrics builder - */ - public Builder updateRestApi(Consumer builderConsumer) { - builderConsumer.accept(apiBuilder); - return this; - } - - /** - * Configure REST API to use. - * - * @param restApi OCI rest API - * @return updated builder - */ - public Builder restApi(OciRestApi restApi) { - this.restApi = restApi; - return this; - } - - String apiVersion() { - return apiVersion; - } - - OciRestApi restApi() { - return restApi; - } - - String hostPrefix() { - return hostPrefix; - } - - String endpoint() { - return endpoint; - } - } -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRxImpl.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRxImpl.java deleted file mode 100644 index 634bbd3d540..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciMetricsRxImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -import java.util.Optional; - -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Single; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciRestApi; - -class OciMetricsRxImpl implements OciMetricsRx { - private final String apiVersion; - private final OciRestApi restAccess; - private final String hostPrefix; - private final Optional endpoint; - - OciMetricsRxImpl(Builder builder) { - this.restAccess = builder.restApi(); - this.apiVersion = builder.apiVersion(); - this.hostPrefix = builder.hostPrefix(); - this.endpoint = Optional.ofNullable(builder.endpoint()); - } - - @Override - public Single postMetricData(PostMetricData.Request request) { - String apiPath = "/" + apiVersion + "/metrics"; - - metrics(request); - - return restAccess.invokeWithResponse(Http.Method.POST, - apiPath, - request, - PostMetricData.Response.builder()); - } - - private void metrics(OciRequestBase request) { - if (request.endpoint().isPresent()) { - return; - } - - endpoint.ifPresent(request::endpoint); - - request.hostFormat(OciMetricsRx.API_HOST_FORMAT) - .hostPrefix(hostPrefix); - } -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciTelemetryInjectionProvider.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciTelemetryInjectionProvider.java deleted file mode 100644 index bbefcb221d3..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/OciTelemetryInjectionProvider.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -import java.util.LinkedList; -import java.util.List; - -import io.helidon.integrations.oci.connect.spi.InjectionProvider; - -/** - * Service provider for {@link io.helidon.integrations.oci.connect.spi.InjectionProvider}. - * @deprecated Do not use directly, this is only used via service loader - */ -@Deprecated -public class OciTelemetryInjectionProvider implements InjectionProvider { - private static final List> INJECTABLES; - - static { - List> injectables = new LinkedList<>(); - - injectables.add(InjectionType.create(OciMetricsRx.class, - (restApi, config) -> OciMetricsRx.builder() - .restApi(restApi) - .config(config) - .build())); - - injectables.add(InjectionType.create(OciMetrics.class, - (restApi, config) -> OciMetrics.create(OciMetricsRx.builder() - .restApi(restApi) - .config(config) - .build()))); - - INJECTABLES = List.copyOf(injectables); - } - - /** - * Public constructor must be present for service loader. - * @deprecated Do not use directly, this is only used via service loader - */ - @Deprecated - public OciTelemetryInjectionProvider() { - } - - @Override - public List> injectables() { - return INJECTABLES; - } -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/PostMetricData.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/PostMetricData.java deleted file mode 100644 index 69baf5008de..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/PostMetricData.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.telemetry; - -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.common.rest.ApiJsonBuilder; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - -/** - * Class to group together all types related to post metric data request and response. - */ -public final class PostMetricData { - private PostMetricData() { - } - - /** - * Post metric data request. - */ - public static class Request extends OciRequestBase { - /** - * Atomic batch - either all succeed, or all fail. - */ - public static final String BATCH_ATOMICITY_ATOMIC = "ATOMIC"; - /** - * Non atomic batch - some may succeed, some may fail. - */ - public static final String BATCH_ATOMICITY_NON_ATOMIC = "NON_ATOMIC"; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Batch atomicity behavior. Requires either partial or full pass of input validation for metric objects in PostMetricData - * requests. The default value of NON_ATOMIC requires a partial pass: at least one metric object in the request must pass - * input validation, and any objects that failed validation are identified in the returned summary, along with their error - * messages. A value of ATOMIC requires a full pass: all metric objects in the request must pass input validation. - *

- * Defaults to {@value #BATCH_ATOMICITY_NON_ATOMIC}. - * - * @param atomicity atomicity to use - * @return updated request - * @see #BATCH_ATOMICITY_ATOMIC - * @see #BATCH_ATOMICITY_NON_ATOMIC - */ - public Request batchAtomicity(String atomicity) { - return add("batchAtomicity", atomicity); - } - - /** - * Raw metric data points to be posted to the monitoring service. - * - * @param metricData metric data - * @return updated request - */ - public Request addMetricData(MetricData metricData) { - return addToArray("metricData", metricData); - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends ApiEntityResponse { - private final int failedMetricsCount; - private final List failedMetrics; - - private Response(Builder builder) { - super(builder); - - JsonObject jsonObject = builder.entity(); - failedMetricsCount = jsonObject.getInt("failedMetricsCount"); - failedMetrics = isPresent(jsonObject, "failedMetrics") - .map(ignored -> { - List failedMetrics = new LinkedList<>(); - JsonArray failedMetricsArray = jsonObject.getJsonArray("failedMetrics"); - for (JsonValue jsonValue : failedMetricsArray) { - failedMetrics.add(FailedMetric.create((JsonObject) jsonValue)); - } - return List.copyOf(failedMetrics); - }).orElseGet(List::of); - } - - static Builder builder() { - return new Builder(); - } - - /** - * Number of metrics that failed to be processed. - * - * @return count - */ - public int failedMetricsCount() { - return failedMetricsCount; - } - - /** - * List of failed metrics. - * - * @return list of failed metrics - */ - public List failedMetrics() { - return failedMetrics; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } - - /** - * The post metric data consists of a set of {@link io.helidon.integrations.oci.telemetry.PostMetricData.MetricData}, - * and each metric data has one or more data points. - */ - public static class MetricDataPoint extends ApiJsonBuilder { - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; - - private MetricDataPoint() { - } - - /** - * Create a new builder. - * - * @return new builder - */ - public static MetricDataPoint builder() { - return new MetricDataPoint(); - } - - /** - * The number of occurrences of the associated value in the set of data. - * - * Default is 1. Value must be greater than zero. - * - * @param count number of occurrences - * @return updated data point - */ - public MetricDataPoint count(int count) { - return add("count", count); - } - - /** - * Numeric value of the metric. - * - * @param value value of the metric - * @return updated data point - */ - public MetricDataPoint value(double value) { - return add("value", value); - } - - /** - * Timestamp of the metric. - * - * @param instant the instant - * @return updated data point - */ - public MetricDataPoint timestamp(TemporalAccessor instant) { - return add("timestamp", FORMATTER.format(instant)); - } - - /** - * Timestamp as date. - * - * @param time the instant - * @return updated data point - */ - public MetricDataPoint timestamp(Date time) { - return timestamp(time.toInstant()); - } - } - - /** - * Failed metric definition. - */ - public static class FailedMetric { - private final String message; - private final JsonObject failedData; - - private FailedMetric(String message, JsonObject failedData) { - this.message = message; - this.failedData = failedData; - } - - static FailedMetric create(JsonObject failedMetric) { - String message = failedMetric.getString("message"); - - return new FailedMetric(message, failedMetric.getJsonObject("metricData")); - } - - /** - * Message describing the problem. - * - * @return error message - */ - public String message() { - return message; - } - - /** - * Json with failed data. - * - * @return json with failed data - */ - public JsonObject failedDataJson() { - return failedData; - } - } - - /** - * Metric data send with post metric data. - */ - public static class MetricData extends ApiJsonBuilder { - /** - * A new builder. - * - * @return builder - */ - public static MetricData builder() { - return new MetricData(); - } - - /** - * The OCID of the compartment to use for metrics. - * - * @param compartmentId compartment OCID - * @return updated data - */ - public MetricData compartmentId(String compartmentId) { - return add("compartmentId", compartmentId); - } - - /** - * A list of metric values with timestamps. At least one data point is required per call. - * - * @param dataPoint data point - * @return updated data - */ - public MetricData addDataPoint(MetricDataPoint dataPoint) { - return addToArray("datapoints", dataPoint); - } - - /** - * A utility method to add data point with count equal to {@code 1}. - * For full control, please use {@link io.helidon.integrations.oci.telemetry.PostMetricData.MetricDataPoint#builder()}. - * - * @param timestamp temporal accessor, such as {@link java.time.Instant} - * @param value Numeric value of the metric. - * @return updated data - */ - public MetricData addDataPoint(TemporalAccessor timestamp, double value) { - return addDataPoint(MetricDataPoint.builder().timestamp(timestamp).value(value)); - } - - /** - * Qualifiers provided in a metric definition. Available dimensions vary by metric namespace. Each dimension takes the - * form - * of a key-value pair. A valid dimension key includes only printable ASCII, excluding periods (.) and spaces. The - * character limit for a dimension key is 256. A valid dimension value includes only Unicode characters. The character - * limit for a dimension value is 256. Empty strings are not allowed for keys or values. Avoid entering confidential - * information. - * - * @param key dimension key - * @param value dimension value - * @return updated data - */ - public MetricData addDimension(String key, String value) { - return addToObject("dimensions", key, value); - } - - /** - * Properties describing metrics. These are not part of the unique fields identifying the metric. Each metadata item takes - * the form of a key-value pair. The character limit for a metadata key is 256. The character limit for a metadata - * value is - * 256. - *

- * Example: {@code "unit": "bytes"}. - * - * @param key name of metadata - * @param value value of metadata - * @return updated data - */ - public MetricData addMetaData(String key, String value) { - return addToObject("metadata", key, value); - } - - /** - * The name of the metric. - * - * A valid name value starts with an alphabetical character and includes only alphanumeric characters, dots, underscores, - * hyphens, and dollar signs. The oci_ prefix is reserved. Avoid entering confidential information. - * - * @param name name - * @return updated request - */ - public MetricData name(String name) { - return add("name", name); - } - - /** - * The source service or application emitting the metric. - * - * A valid namespace value starts with an alphabetical character and includes only alphanumeric characters and - * underscores. - * The "oci_" prefix is reserved. Avoid entering confidential information. - * - * @param namespace namespace - * @return updated request - */ - public MetricData namespace(String namespace) { - return add("namespace", namespace); - } - - /** - * Resource group to assign to the metric. A resource group is a custom string that you can match when retrieving custom - * metrics. Only one resource group can be applied per metric. A valid resourceGroup value starts with an alphabetical - * character and includes only alphanumeric characters, periods (.), underscores (_), hyphens (-), and dollar signs ($). - * Avoid entering confidential information. - * - * @param resourceGroup resource group - * @return updated request - */ - public MetricData resourceGroup(String resourceGroup) { - return add("resourceGroup", resourceGroup); - } - } -} diff --git a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/package-info.java b/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/package-info.java deleted file mode 100644 index a6e1a09032a..00000000000 --- a/integrations/oci/telemetry/src/main/java/io/helidon/integrations/oci/telemetry/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ - -/** - * Integration with OCI Telemetry. - * - * @see io.helidon.integrations.oci.telemetry.OciMetrics - * @see io.helidon.integrations.oci.telemetry.OciMetricsRx - */ -package io.helidon.integrations.oci.telemetry; diff --git a/integrations/oci/telemetry/src/main/java/module-info.java b/integrations/oci/telemetry/src/main/java/module-info.java deleted file mode 100644 index f3e46b83844..00000000000 --- a/integrations/oci/telemetry/src/main/java/module-info.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -/** - * Integration with OCI Telemetry. - * - * @see io.helidon.integrations.oci.telemetry.OciMetrics - * @see io.helidon.integrations.oci.telemetry.OciMetricsRx - */ -module io.helidon.integrations.oci.telemetry { - requires jakarta.json; - - requires io.helidon.common.reactive; - requires io.helidon.integrations.common.rest; - requires transitive io.helidon.integrations.oci.connect; - requires io.helidon.common.http; - requires io.helidon.common; - requires io.helidon.config; - - exports io.helidon.integrations.oci.telemetry; - - provides io.helidon.integrations.oci.connect.spi.InjectionProvider - with io.helidon.integrations.oci.telemetry.OciTelemetryInjectionProvider; - - opens io.helidon.integrations.oci.telemetry to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/telemetry/src/main/resources/META-INF/helidon/native-image/weld-proxies.json b/integrations/oci/telemetry/src/main/resources/META-INF/helidon/native-image/weld-proxies.json deleted file mode 100644 index 76dab3e2916..00000000000 --- a/integrations/oci/telemetry/src/main/resources/META-INF/helidon/native-image/weld-proxies.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.telemetry.OciMetrics" - ] - }, - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.telemetry.OciMetricsRx" - ] - } -] \ No newline at end of file diff --git a/integrations/oci/vault-health/pom.xml b/integrations/oci/vault-health/pom.xml deleted file mode 100644 index d7ba385f701..00000000000 --- a/integrations/oci/vault-health/pom.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-vault-health - Helidon Integrations OCI Vault Health Check - - OCI Vault Health Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-vault - - - io.helidon.common - helidon-common - - - io.helidon.config - helidon-config - - - io.helidon.health - helidon-health - - - io.helidon.health - helidon-health-common - - - io.helidon.config - helidon-config-yaml - test - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - true - - - - org.eclipse.microprofile.config - microprofile-config-api - provided - true - - - org.junit.jupiter - junit-jupiter-api - test - - - org.hamcrest - hamcrest-all - test - - - diff --git a/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/OciVaultHealthCheck.java b/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/OciVaultHealthCheck.java deleted file mode 100644 index bc87dd176b7..00000000000 --- a/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/OciVaultHealthCheck.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault.health; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import io.helidon.config.Config; -import io.helidon.health.common.BuiltInHealthCheck; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.vault.GetVault; -import io.helidon.integrations.oci.vault.OciVault; -import io.helidon.integrations.oci.vault.OciVaultRx; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.health.HealthCheck; -import org.eclipse.microprofile.health.HealthCheckResponse; -import org.eclipse.microprofile.health.HealthCheckResponseBuilder; -import org.eclipse.microprofile.health.Liveness; - -import static io.helidon.common.http.Http.Status.OK_200; - -/** - * Liveness check for an OCI's Vault. Reads OCI properties from '~/.oci/config'. - */ -@Liveness -@ApplicationScoped // this will be ignored if not within CDI -@BuiltInHealthCheck -public final class OciVaultHealthCheck implements HealthCheck { - private static final Logger LOGGER = Logger.getLogger(OciVaultHealthCheck.class.getName()); - - private final List vaultIds; - private final OciVault ociVault; - - @Inject - OciVaultHealthCheck(@ConfigProperty(name = "oci.vault.healthchecks") List vaultIds, - OciVault ociVault) { - this.vaultIds = vaultIds; - this.ociVault = ociVault; - } - - private OciVaultHealthCheck(Builder builder) { - this.vaultIds = builder.vaultIds; - this.ociVault = builder.vault; - } - - /** - * Create a new fluent API builder to configure a new health check. - * - * @return builder instance - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create an instance. - * - * @param config the config. - * @return an instance. - */ - public static OciVaultHealthCheck create(Config config) { - return builder().config(config).build(); - } - - /** - * Checks that the OCI vault is accessible, if defined. Will report error only if not - * defined or not accessible. Can block since all health checks are called asynchronously. - * - * @return a response - */ - @Override - public HealthCheckResponse call() { - HealthCheckResponseBuilder builder = HealthCheckResponse.named("vault"); - - boolean status = true; - for (String vaultId : vaultIds) { - try { - ApiOptionalResponse r = ociVault.getVault(GetVault.Request.builder().vaultId(vaultId)); - LOGGER.fine(() -> "OCI vault health check " + vaultId + " returned status code " + r.status().code()); - r.entity().ifPresentOrElse(e -> { - String id = e.displayName() != null && !e.displayName().isEmpty() ? e.displayName() : vaultId; - builder.withData(id, r.status().code()); - }, () -> builder.withData(vaultId, r.status().code())); - status = status && r.status().equals(OK_200); - } catch (Throwable t) { - LOGGER.fine(() -> "OCI vault health check " + vaultId + " exception " + t.getMessage()); - status = false; - builder.withData(vaultId, t.getMessage()); - } - } - - builder.status(status); - return builder.build(); - } - - /** - * Fluent API builder for {@link OciVaultHealthCheck}. - */ - public static final class Builder implements io.helidon.common.Builder { - - private OciVaultRx vaultRx; - private OciVault vault; - private final List vaultIds = new ArrayList<>(); - - private Builder() { - } - - @Override - public OciVaultHealthCheck build() { - if (vaultRx == null) { - vaultRx = OciVaultRx.create(); - } - this.vault = OciVault.create(vaultRx); - return new OciVaultHealthCheck(this); - } - - /** - * Set up this builder using config. - * - * @param config the config. - * @return the builder. - */ - public Builder config(Config config) { - config.get("oci.vault.healthchecks").asList(String.class).ifPresent(this.vaultIds::addAll); - return this; - } - - /** - * Set the vault's OCID. - * - * @param vaultId vault ID. - * @return the builder. - */ - public Builder addVaultId(String vaultId) { - this.vaultIds.add(vaultId); - return this; - } - - /** - * Set the vault client. - * - * @param vaultRx vault client. - * @return the builder. - */ - public Builder ociVault(OciVaultRx vaultRx) { - this.vaultRx = vaultRx; - return this; - } - } -} diff --git a/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/package-info.java b/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/package-info.java deleted file mode 100644 index dd2a34cde06..00000000000 --- a/integrations/oci/vault-health/src/main/java/io/helidon/integrations/oci/vault/health/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Vault Health. - */ -package io.helidon.integrations.oci.vault.health; diff --git a/integrations/oci/vault-health/src/main/java/module-info.java b/integrations/oci/vault-health/src/main/java/module-info.java deleted file mode 100644 index fb317ea2015..00000000000 --- a/integrations/oci/vault-health/src/main/java/module-info.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -/** - * OCI Vault Health. - */ -module io.helidon.integrations.oci.vault.health { - requires java.logging; - requires io.helidon.integrations.oci.connect; - requires io.helidon.common; - - requires transitive io.helidon.health; - requires transitive io.helidon.health.common; - requires transitive io.helidon.config; - requires transitive io.helidon.integrations.oci.vault; - - requires static microprofile.config.api; - requires static jakarta.cdi; - requires static jakarta.inject; - - exports io.helidon.integrations.oci.vault.health; - - opens io.helidon.integrations.oci.vault.health to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/vault-health/src/main/resources/META-INF/beans.xml b/integrations/oci/vault-health/src/main/resources/META-INF/beans.xml deleted file mode 100644 index dbf3e648c1e..00000000000 --- a/integrations/oci/vault-health/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/integrations/oci/vault/pom.xml b/integrations/oci/vault/pom.xml deleted file mode 100644 index c2a156d3616..00000000000 --- a/integrations/oci/vault/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - 4.0.0 - - io.helidon.integrations.oci - helidon-integrations-oci-project - 3.0.0-SNAPSHOT - - - helidon-integrations-oci-vault - Helidon Integrations OCI Vault - - OCI Vault Support - - - - io.helidon.integrations.oci - helidon-integrations-oci-connect - - - io.helidon.common - helidon-common - - - io.helidon.config - helidon-config - - - io.helidon.security - helidon-security - - - io.helidon.config - helidon-config-metadata - provided - true - - - io.helidon.config - helidon-config-metadata-processor - provided - true - - - \ No newline at end of file diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/CreateSecret.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/CreateSecret.java deleted file mode 100644 index 7ad37a0df93..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/CreateSecret.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.common.rest.ApiJsonBuilder; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonObject; - -/** - * Request and response for creating a Vault secret. - */ -public final class CreateSecret { - private CreateSecret() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The OCID of the vault where you want to create the secret. - * Required. - * - * @param vaultOcid vault OCI - * @return updated request - */ - public Request vaultId(String vaultOcid) { - return add("vaultId", vaultOcid); - } - - /** - * A user-friendly name for the secret. Secret names should be unique within a vault. Avoid entering confidential - * information. Valid characters are uppercase or lowercase letters, numbers, hyphens, underscores, and periods. - * Required. - * - * @param name name of the secret - * @return updated request - */ - public Request secretName(String name) { - return add("secretName", name); - } - - /** - * A brief description of the secret. Avoid entering confidential information. - * Optional. - * - * @param description description - * @return updated request - */ - public Request description(String description) { - return add("description", description); - } - - /** - * The OCID of the master encryption key that is used to encrypt the secret. You must specify a symmetric key to - * encrypt the secret during import to the vault. You cannot encrypt secrets with asymmetric keys. Furthermore, the key - * must exist in the vault that you specify. - * This is required, even though the API docs mark it as optional. - * - * @param encryptionKeyOcid OCID of the encryption key - * @return updated request - */ - public Request encryptionKeyId(String encryptionKeyOcid) { - return add("keyId", encryptionKeyOcid); - } - - /** - * Content of the secret. - * - * @param secretContent content - * @return updated request - */ - public Request secretContent(SecretContent secretContent) { - return add("secretContent", secretContent); - } - - /** - * The OCID of the compartment where you want to create the secret. - * Required. - * - * @param compartmentOcid compartment OCID - * @return updated request - */ - public Request compartmentId(String compartmentOcid) { - return add("compartmentId", compartmentOcid); - } - } - - /** - * The content of the secret and metadata to help identify it. - */ - public static class SecretContent extends ApiJsonBuilder { - private SecretContent() { - } - - /** - * Create a new secret content. - * @return a new builder - */ - public static SecretContent builder() { - return new SecretContent(); - } - - /** - * Create new content from plain text secret. - * @param plainTextSecret plain text - * @return a new builder with plain text content - */ - public static SecretContent create(String plainTextSecret) { - return builder().content(plainTextSecret); - } - - /** - * Names should be unique within a secret. Valid characters are uppercase or lowercase letters, numbers, hyphens, - * underscores, and periods. - * Optional. - * @param name name of the secret - * @return updated builder - */ - public SecretContent name(String name) { - return add("name", name); - } - - /** - * The text content of the secret. - * Optional. - * - * @param content content of the secret - * @return updated builder - * @see #contentBase64(String) - */ - public SecretContent content(String content) { - return contentBase64(Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8))); - } - - /** - * The base64-encoded content of the secret. - * Optional. - * - * @param base64Content content - * @return updated builder - * @see #content(String) - */ - public SecretContent contentBase64(String base64Content) { - add("contentType", "BASE64"); - return add("content", base64Content); - } - - /** - * The rotation state of the secret content. The default is CURRENT, meaning that the secret is currently in use. A - * secret version that you mark as PENDING is staged and available for use, but you don't yet want to rotate it into - * current, active use. For example, you might create or update a secret and mark its rotation state as PENDING if you - * haven't yet updated the secret on the target system. When creating a secret, only the value CURRENT is applicable, - * although the value LATEST is also automatically applied. When updating a secret, you can specify a version's - * rotation state as either CURRENT or PENDING. - * Optional. - * - * @param stage either {@link SecretStage#CURRENT} or {@link SecretStage#PENDING} are allowed - * @return updated builder - */ - public SecretContent stage(SecretStage stage) { - return add("stage", stage.toString()); - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static final class Response extends ApiEntityResponse { - private final Secret secret; - - private Response(Builder builder) { - super(builder); - this.secret = Secret.create(builder.entity()); - } - - static Builder builder() { - return new Builder(); - } - - /** - * Metadata of the created secret. - * - * @return secret - */ - public Secret secret() { - return secret; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Decrypt.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Decrypt.java deleted file mode 100644 index 457da2a3ed2..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Decrypt.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.Optional; - -import io.helidon.common.Base64Value; -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonObject; - -/** - * Encrypt request and response. - */ -public final class Decrypt { - private Decrypt() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - - private String keyId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The data to decrypt. - * - * @param cipherText encrypted data - * @return updated request - */ - public Request cipherText(String cipherText) { - return add("ciphertext", cipherText); - } - - /** - * The OCID of the key to encrypt with. - * Required. - * - * @param keyOcid OCID of the key - * @return updated request - */ - public Request keyId(String keyOcid) { - this.keyId = keyOcid; - return add("keyId", keyOcid); - } - - /** - * Information that can be used to provide an encryption context for the encrypted data. The length of the string - * representation of the associated data must be fewer than 4096 characters. - * Optional. - * - * @param contextData context - * @return updated request - */ - public Request context(String contextData) { - return add("associatedData", contextData); - } - - /** - * The encryption algorithm to use to encrypt and decrypt data with a customer-managed key. AES_256_GCM indicates that - * the key is a symmetric key that uses the Advanced Encryption Standard (AES) algorithm and that the mode of - * encryption is the Galois/Counter Mode (GCM). RSA_OAEP_SHA_1 indicates that the key is an asymmetric key that uses - * the RSA encryption algorithm and uses Optimal Asymmetric Encryption Padding (OAEP). RSA_OAEP_SHA_256 indicates that - * the key is an asymmetric key that uses the RSA encryption algorithm with a SHA-256 hash and uses OAEP. - * Optional, defaults to {@code AES_256_GCM}. - * - * @param algorithm algorithm to use - * @return updated request - * @see io.helidon.integrations.oci.vault.Encrypt.Request#ALGORITHM_AES_256_GCM - */ - public Request algorithm(String algorithm) { - return add("encryptionAlgorithm", algorithm); - } - - /** - * The OCID of the key version used to encrypt the ciphertext. - * Optional. - * - * @param versionOcid OCID of the key version - * @return updated request - */ - public Request keyVersionId(String versionOcid) { - return add("keyVersionId", versionOcid); - } - - String keyId() { - if (keyId == null) { - throw new OciApiException("Encrypt.Request keyId must be defined"); - } - return keyId; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static final class Response extends ApiEntityResponse { - private final Base64Value decrypted; - private final String checksum; - private final Optional encryptionAlgorithm; - private final Optional keyId; - private final Optional keyVersionId; - - private Response(Builder builder) { - super(builder); - - JsonObject json = builder.entity(); - this.decrypted = Base64Value.createFromEncoded(json.getString("plaintext")); - this.checksum = json.getString("plaintextChecksum"); - this.encryptionAlgorithm = toString(json, "encryptionAlgorithm"); - this.keyId = toString(json, "keyId"); - this.keyVersionId = toString(json, "keyVersionId"); - } - - static Builder builder() { - return new Builder(); - } - - /** - * Decrypted secret. - * - * @return decrypted value - */ - public Base64Value decrypted() { - return decrypted; - } - - /** - * Data checksum. - * - * @return checksum - */ - public String checksum() { - return checksum; - } - - /** - * Algorithm used. - * - * @return algorithm - */ - public Optional encryptionAlgorithm() { - return encryptionAlgorithm; - } - - /** - * Decryption key ID. - * - * @return key ID - */ - public Optional keyId() { - return keyId; - } - - /** - * Decryption key version ID. - * - * @return key version ID - */ - public Optional keyVersionId() { - return keyVersionId; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/DeleteSecret.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/DeleteSecret.java deleted file mode 100644 index ae954c5c971..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/DeleteSecret.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.time.Instant; - -import io.helidon.integrations.common.rest.ApiException; -import io.helidon.integrations.common.rest.ApiResponse; -import io.helidon.integrations.oci.connect.OciRequestBase; - -/** - * Delete Secret request and response. - */ -public final class DeleteSecret { - private DeleteSecret() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - private String secretId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Create request for a secret ID. - * - * @param secretId secret OCID - * @return a new request - * @see #secretId(String) - */ - public static Request create(String secretId) { - return builder().secretId(secretId); - } - - /** - * Secret OCID. - * Required. - * - * @param secretId id of secret to delte - * @return updated request - */ - public Request secretId(String secretId) { - this.secretId = secretId; - return this; - } - - /** - * Configure the time of deletion. - * - * @param whenToDelete when to delete this secret - * @return updated request - */ - public Request timeOfDeletion(Instant whenToDelete) { - return add("timeOfDeletion", whenToDelete); - } - - /** - * The configured secret ID. - * - * @return secret ID - */ - public String secretId() { - if (secretId == null) { - throw new ApiException("secretId is a mandatory parameter for DeleteSecret"); - } - return secretId; - } - } - - /** - * Response object for responses without an entity. - */ - public static final class Response extends ApiResponse { - private Response(Builder builder) { - super(builder); - } - - static Builder builder() { - return new Builder(); - } - - static final class Builder extends ApiResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Encrypt.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Encrypt.java deleted file mode 100644 index 4525f790645..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Encrypt.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.Optional; - -import io.helidon.common.Base64Value; -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonObject; - -/** - * Encrypt request and response. - */ -public final class Encrypt { - private Encrypt() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - /** - * Default encryption algorithm used by encryption/decryption is {@value}. - */ - public static final String ALGORITHM_AES_256_GCM = "AES_256_GCM"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_RSA_OAEP_SHA_1 = "RSA_OAEP_SHA_1"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_RSA_OAEP_SHA_256 = "RSA_OAEP_SHA_256"; - - private String keyId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The data to encrypt. - * - * @param value value to encrypt - * @return updated request - * @see Base64Value#create(String) - * @see Base64Value#create(byte[]) - */ - public Request data(Base64Value value) { - return add("plaintext", value.toBase64()); - } - - /** - * The OCID of the key to encrypt with. - * Required. - * - * @param keyOcid OCID of the key - * @return updated request - */ - public Request keyId(String keyOcid) { - this.keyId = keyOcid; - return add("keyId", keyOcid); - } - - /** - * Information that can be used to provide an encryption context for the encrypted data. The length of the string - * representation of the associated data must be fewer than 4096 characters. - * Optional. - * - * @param contextData context - * @return updated request - */ - public Request context(String contextData) { - return add("associatedData", contextData); - } - - /** - * The encryption algorithm to use to encrypt and decrypt data with a customer-managed key. AES_256_GCM indicates that - * the key is a symmetric key that uses the Advanced Encryption Standard (AES) algorithm and that the mode of - * encryption is the Galois/Counter Mode (GCM). RSA_OAEP_SHA_1 indicates that the key is an asymmetric key that uses - * the RSA encryption algorithm and uses Optimal Asymmetric Encryption Padding (OAEP). RSA_OAEP_SHA_256 indicates that - * the key is an asymmetric key that uses the RSA encryption algorithm with a SHA-256 hash and uses OAEP. - * Optional, defaults to {@code AES_256_GCM}. - * - * @param algorithm algorithm to use - * @return updated request - */ - public Request algorithm(String algorithm) { - return add("encryptionAlgorithm", algorithm); - } - - /** - * The OCID of the key version used to encrypt the ciphertext. - * Optional. - * - * @param versionOcid OCID of the key version - * @return updated request - */ - public Request keyVersionId(String versionOcid) { - return add("keyVersionId", versionOcid); - } - - String keyId() { - if (keyId == null) { - throw new OciApiException("Encrypt.Request keyId must be defined"); - } - return keyId; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static final class Response extends ApiEntityResponse { - private final String cipherText; - private final Optional encryptionAlgorithm; - private final Optional keyId; - private final Optional keyVersionId; - - private Response(Builder builder) { - super(builder); - - JsonObject json = builder.entity(); - this.cipherText = json.getString("ciphertext"); - this.encryptionAlgorithm = toString(json, "encryptionAlgorithm"); - this.keyId = toString(json, "keyId"); - this.keyVersionId = toString(json, "keyVersionId"); - } - - static Builder builder() { - return new Builder(); - } - - /** - * Cipher text that can be passed to another service and then used - * to obtain the decrypted secret. - * - * @return cipher text - */ - public String cipherText() { - return cipherText; - } - - /** - * Encryption algorithm used to encrypt the secret. - * - * @return encryption algorithm - */ - public Optional encryptionAlgorithm() { - return encryptionAlgorithm; - } - - /** - * Encryption key OCID. - * - * @return key OCID - */ - public Optional keyId() { - return keyId; - } - - /** - * Encryption key version OCID. - * - * @return key version ocid - */ - public Optional keyVersionId() { - return keyVersionId; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetKey.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetKey.java deleted file mode 100644 index ec0ac69cb9f..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetKey.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.time.Instant; -import java.util.Optional; - -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Get Key request and response. - */ -public final class GetKey { - private GetKey() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private String keyId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Create a new request for Key OCID. - * - * @param keyId key OCID - * @return a new request - */ - public static Request create(String keyId) { - return builder().keyId(keyId); - } - - /** - * Key OCID. - * - * @param keyId key OCID - * @return updated request - */ - public Request keyId(String keyId) { - this.keyId = keyId; - return this; - } - - String keyId() { - if (keyId == null) { - throw new OciApiException("GetKey.Request keyId must be defined"); - } - return keyId; - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final String keyId; - private final String currentKeyVersionId; - private final String compartmentId; - private final String displayName; - private final String lifecycleState; - private final Instant created; - private final String vaultId; - - private Response(JsonObject json) { - this.keyId = json.getString("id"); - this.currentKeyVersionId = json.getString("currentKeyVersion"); - this.compartmentId = json.getString("compartmentId"); - this.displayName = json.getString("displayName"); - this.lifecycleState = json.getString("lifecycleState"); - this.created = getInstant(json, "timeCreated"); - this.vaultId = json.getString("vaultId"); - } - - static Response create(JsonObject json) { - return new Response(json); - } - - /** - * Key OCID. - * - * @return key OCID - */ - public String keyId() { - return keyId; - } - - /** - * The OCID of the key version used in cryptographic operations. During key rotation, the service might be in a - * transitional state where this or a newer key version are used intermittently. The currentKeyVersion property is - * updated when the service is guaranteed to use the new key version for all subsequent encryption operations. - * - * @return current key version ID - */ - public String currentKeyVersionId() { - return currentKeyVersionId; - } - - /** - * The OCID of the compartment that contains this master encryption key. - * - * @return compartment ID - */ - public String compartmentId() { - return compartmentId; - } - - /** - * A user-friendly name for the key. It does not have to be unique, and it is changeable. Avoid entering confidential information. - * - * @return display name - */ - public String displayName() { - return displayName; - } - - /** - * The key's current lifecycle state. - * - * @return lifecycle state - */ - public String lifecycleState() { - return lifecycleState; - } - - /** - * The date and time the key was created. - * - * @return created instant - */ - public Instant created() { - return created; - } - - /** - * The OCID of the vault that contains this key. - * - * @return Vault ID - */ - public String vaultId() { - return vaultId; - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecret.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecret.java deleted file mode 100644 index 5d416433111..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecret.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.integrations.common.rest.ApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -/** - * Request and response for getting secret metadata. - */ -public final class GetSecret { - private GetSecret() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private String secretId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Create a new request for a secret OCID. - * - * @param secretOcid secret OCID - * @return a new request - * - * @see #secretId(String) - */ - public static Request create(String secretOcid) { - return builder().secretId(secretOcid); - } - - /** - * Secret OCID to get. - * - * @param secretId secret OCID - * @return updated request - */ - public Request secretId(String secretId) { - this.secretId = secretId; - return this; - } - - String secretId() { - if (secretId == null) { - throw new ApiException("secretId is mandatory in GetSecret.Request"); - } - return secretId; - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecretBundle.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecretBundle.java deleted file mode 100644 index a437f803845..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetSecretBundle.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import io.helidon.integrations.common.rest.ApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonObject; - -/** - * Request and response for getting secret. - */ -public class GetSecretBundle { - private GetSecretBundle() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private String secretId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Create a new request for secret ID. - * - * @param secretId secret OCID - * @return a new request - */ - public static Request create(String secretId) { - return builder().secretId(secretId); - } - - /** - * Secret OCID. - * Required. - * - * @param secretId secret OCID - * @return updated request - */ - public Request secretId(String secretId) { - this.secretId = secretId; - return this; - } - - /** - * Secret version number. - * - * @param versionNumber version number - * @return updated request - */ - public Request versionNumber(int versionNumber) { - return addQueryParam("versionNumber", String.valueOf(versionNumber)); - } - - /** - * Secret version name. - * - * @param versionName version name - * @return updated request - */ - public Request versionName(String versionName) { - return addQueryParam("versionName", versionName); - } - - /** - * Secret stage. - * - * @param stage stage - * @return updated request - */ - public Request stage(SecretStage stage) { - return addQueryParam("stage", stage.toString()); - } - - String secretId() { - if (secretId == null) { - throw new ApiException("secretId is a mandatory parameter for GetSecretBundle"); - } - return secretId; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final Map metadata; - private final String secretId; - private final List stages; - private final Optional timeCreated; - private final Optional timeDeleted; - private final Optional expirationTime; - private final Optional versionName; - private final int versionNumber; - private final Optional secretContent; - - private Response(JsonObject json) { - this.metadata = toMap(json, "metadata"); - this.secretId = json.getString("secretId"); - this.timeCreated = toInstant(json, "timeCreated"); - this.timeDeleted = toInstant(json, "timeOfDeletion"); - this.expirationTime = toInstant(json, "timeOfExpiry"); - this.versionName = toString(json, "versionName"); - this.versionNumber = json.getInt("versionNumber"); - this.secretContent = toObject(json, "secretBundleContent") - .flatMap(bundle -> { - String contentType = bundle.getString("contentType"); - if ("BASE64".equals(contentType)) { - return toBytesBase64(bundle, "content"); - } - throw new ApiException("Unsupported secret content type: " + contentType + ", only BASE64 is supported"); - }); - this.stages = toList(json, "stages") - .stream() - .map(SecretStage::valueOf) - .collect(Collectors.toList()); - - } - - static Response create(JsonObject json) { - return new Response(json); - } - - /** - * Customer-provided contextual metadata for the secret. - * - * @return metadata - */ - public Map metadata() { - return metadata; - } - - /** - * The OCID of the secret. - * - * @return secret OCID - */ - public String secretId() { - return secretId; - } - - /** - * A list of possible rotation states for the secret version. - * - * @return stages - */ - public List stages() { - return stages; - } - - /** - * The time when the secret bundle was created. - * - * @return time of creation - */ - public Optional timeCreated() { - return timeCreated; - } - - /** - * The time the secret would be deleted. - * - * @return time of deletion - */ - public Optional timeDeleted() { - return timeDeleted; - } - - /** - * An optional property indicating when the secret version will expire. - * - * @return expiration time - */ - public Optional expirationTime() { - return expirationTime; - } - - /** - * The name of the secret version. Labels are unique across the different versions of a particular secret. - * - * @return version name - */ - public Optional versionName() { - return versionName; - } - - /** - * The version number of the secret. - * - * @return version number - */ - public int versionNumber() { - return versionNumber; - } - - /** - * The content of the secrets, as bytes. - * - * @return byte value if present - */ - public Optional secretBytes() { - return secretContent; - } - - /** - * The content of the secrets as a string. - * This method will attempt to create a string from the provided bytes using UTF-8 encoding. - * - * @return string value if present - */ - public Optional secretString() { - return secretContent.map(it -> new String(it, StandardCharsets.UTF_8)); - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetVault.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetVault.java deleted file mode 100644 index bf3771b9748..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/GetVault.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.time.Instant; -import java.util.Optional; - -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - -/** - * Get Vault request and response. - */ -public final class GetVault { - private GetVault() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static class Request extends OciRequestBase { - private String vaultId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * Create a request for a vault OCID. - * - * @param vaultId vault OCID - * @return a new request - */ - public static Request create(String vaultId) { - return builder().vaultId(vaultId); - } - - /** - * Vault OCID. - * - * @param vaultId vault ID - * @return updated request - */ - public Request vaultId(String vaultId) { - this.vaultId = vaultId; - return this; - } - - String vaultId() { - if (vaultId == null) { - throw new OciApiException("GetVault.Request vaultId must be defined"); - } - return vaultId; - } - - @Override - public Optional toJson(JsonBuilderFactory factory) { - return Optional.empty(); - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static class Response extends OciResponseParser { - private final String vaultId; - private final String compartmentId; - private final String cryptoEndpoint; - private final String managementEndpoint; - private final String displayName; - private final String lifecycleState; - private final Instant created; - private final String vaultType; - private final String wrappingKeyId; - - private Response(JsonObject json) { - this.vaultId = json.getString("id"); - this.compartmentId = json.getString("compartmentId"); - this.cryptoEndpoint = json.getString("cryptoEndpoint"); - this.managementEndpoint = json.getString("managementEndpoint"); - this.displayName = json.getString("displayName"); - this.lifecycleState = json.getString("lifecycleState"); - this.created = getInstant(json, "timeCreated"); - this.vaultType = json.getString("vaultType"); - this.wrappingKeyId = json.getString("wrappingkeyId"); - } - - static Response create(JsonObject json) { - return new Response(json); - } - - /** - * The OCID of the compartment that contains this vault. - * - * @return compartment ID - */ - public String compartmentId() { - return compartmentId; - } - - /** - * A user-friendly name for the vault. It does not have to be unique, and it is changeable. Avoid entering confidential - * information. - * - * @return display name - */ - public String displayName() { - return displayName; - } - - /** - * The vault's current lifecycle state. - * - * @return lifecycle state - */ - public String lifecycleState() { - return lifecycleState; - } - - /** - * The date and time the vault was created. - * - * @return created instant - */ - public Instant created() { - return created; - } - - /** - * The OCID of the vault. - * - * @return Vault ID - */ - public String vaultId() { - return vaultId; - } - - /** - * The service endpoint to perform cryptographic operations against. Cryptographic operations include Encrypt, Decrypt, - * and GenerateDataEncryptionKey operations. - * - * @return cryptographic endpoint - */ - public String cryptoEndpoint() { - return cryptoEndpoint; - } - - /** - * The service endpoint to perform management operations against. Management operations include "Create," "Update," - * "List," "Get," and "Delete" operations. - * - * @return management endpoint - */ - public String managementEndpoint() { - return managementEndpoint; - } - - /** - * The type of vault. Each type of vault stores the key with different degrees of isolation and has different options - * and pricing. - * - * Allowed values are: - * - *

    - *
  • {@code VIRTUAL_PRIVATE}
  • - *
  • {@code DEFAULT}
  • - *
- * - * @return vault type - */ - public String vaultType() { - return vaultType; - } - - /** - * The OCID of the vault's wrapping key. - * - * @return wrapping key id - */ - public String wrappingKeyId() { - return wrappingKeyId; - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVault.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVault.java deleted file mode 100644 index 48ae131f69e..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVault.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -/** - * Blocking API to access OCI Vault. - * All methods block the current thread. This implementation is not suitable for reactive programming. - * Use {@link io.helidon.integrations.oci.vault.OciVaultRx} in reactive code. - */ -public interface OciVault { - /** - * Create blocking Vault from its reactive counterpart. - * When within an environment that supports injection, such as CDI, this class can be simply injected. - * - * @param reactive reactive Vault - * @return blocking Vault - */ - static OciVault create(OciVaultRx reactive) { - return new OciVaultImpl(reactive); - } - - /** - * Gets information about the specified secret. - * - * @param request get secret request - * @return future with secret response or exception - */ - ApiOptionalResponse getSecret(GetSecret.Request request); - - /** - * Create a new secret. - * - * @param request create secret request - * @return future with create secret response or exception - */ - CreateSecret.Response createSecret(CreateSecret.Request request); - - /** - * Gets information about the specified secret. - * - * @param request get secret bundle request - * @return future with response or error - */ - ApiOptionalResponse getSecretBundle(GetSecretBundle.Request request); - - /** - * Schedules a secret deletion. - * - * @param request delete secret request - * @return future with response or error - */ - DeleteSecret.Response deleteSecret(DeleteSecret.Request request); - - /** - * Encrypt data. - * - * @param request encryption request - * @return future with encrypted data - */ - Encrypt.Response encrypt(Encrypt.Request request); - - /** - * Decrypt data. - * - * @param request decryption request - * @return future with decrypted data - */ - Decrypt.Response decrypt(Decrypt.Request request); - - /** - * Sign a message. - * - * @param request signature request - * @return signature response - */ - Sign.Response sign(Sign.Request request); - - /** - * Verify a message signature. - * - * @param request verification request - * @return verification response - */ - Verify.Response verify(Verify.Request request); - - /** - * Get key metadata. - * - * @param request get key request - * @return get key response - */ - ApiOptionalResponse getKey(GetKey.Request request); - - /** - * Get Vault metadata. - * - * @param request get vault request - * @return get vault response - */ - ApiOptionalResponse getVault(GetVault.Request request); -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultImpl.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultImpl.java deleted file mode 100644 index 9208a94187d..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.integrations.common.rest.ApiOptionalResponse; - -class OciVaultImpl implements OciVault { - private final OciVaultRx delegate; - - OciVaultImpl(OciVaultRx delegate) { - this.delegate = delegate; - } - - @Override - public ApiOptionalResponse getSecret(GetSecret.Request request) { - return delegate.getSecret(request).await(); - } - - @Override - public CreateSecret.Response createSecret(CreateSecret.Request request) { - return delegate.createSecret(request).await(); - } - - @Override - public ApiOptionalResponse getSecretBundle(GetSecretBundle.Request request) { - return delegate.getSecretBundle(request).await(); - } - - @Override - public DeleteSecret.Response deleteSecret(DeleteSecret.Request request) { - return delegate.deleteSecret(request).await(); - } - - @Override - public Encrypt.Response encrypt(Encrypt.Request request) { - return delegate.encrypt(request).await(); - } - - @Override - public Decrypt.Response decrypt(Decrypt.Request request) { - return delegate.decrypt(request).await(); - } - - @Override - public Sign.Response sign(Sign.Request request) { - return delegate.sign(request).await(); - } - - @Override - public Verify.Response verify(Verify.Request request) { - return delegate.verify(request).await(); - } - - @Override - public ApiOptionalResponse getKey(GetKey.Request request) { - return delegate.getKey(request).await(); - } - - @Override - public ApiOptionalResponse getVault(GetVault.Request request) { - return delegate.getVault(request).await(); - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultInjectionProvider.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultInjectionProvider.java deleted file mode 100644 index ce05f50117e..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultInjectionProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.LinkedList; -import java.util.List; - -import io.helidon.integrations.oci.connect.spi.InjectionProvider; - -/** - * Service provider for {@link io.helidon.integrations.oci.connect.spi.InjectionProvider}. - * Only used by a service loader. - * @deprecated do not use directly - */ -@Deprecated -public class OciVaultInjectionProvider implements InjectionProvider { - private static final List> INJECTABLES; - - static { - List> injectables = new LinkedList<>(); - - injectables.add(InjectionType.create(OciVaultRx.class, - (restApi, config) -> OciVaultRx.builder() - .restApi(restApi) - .config(config) - .build())); - - injectables.add(InjectionType.create(OciVault.class, - (restApi, config) -> OciVault.create(OciVaultRx.builder() - .restApi(restApi) - .config(config) - .build()))); - - INJECTABLES = List.copyOf(injectables); - } - - /** - * This constructor is only intended for service loader. - * DO NOT USE DIRECTLY. - * @deprecated do not use - */ - @Deprecated - public OciVaultInjectionProvider() { - } - - @Override - public List> injectables() { - return INJECTABLES; - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRx.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRx.java deleted file mode 100644 index 615d8f47537..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRx.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.function.Consumer; - -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.config.metadata.Configured; -import io.helidon.config.metadata.ConfiguredOption; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciConfigProfile; -import io.helidon.integrations.oci.connect.OciRestApi; - -/** - * Reactive APIs for OCI Vault. - */ -public interface OciVaultRx { - /** - * Default endpoint format for KMS. - */ - String ENDPOINT_FORMAT = "%s://%s.%s.%s"; - - /** - * Default endpoint format for secrets. - */ - String OCI_ENDPOINT_FORMAT = "%s://%s.%s.oci.%s"; - - /** - * Version of Secret API supported by this client. - */ - String SECRET_API_VERSION = "20180608"; - /** - * Version of Secret Bundle API supported by this client. - */ - String SECRET_BUNDLE_API_VERSION = "20190301"; - - /** - * Host name prefix. - */ - String VAULTS_HOST_PREFIX = "vaults"; - - /** - * Host name prefix for key management service (KMS). - */ - String KMS_HOST_PREFIX = "kms"; - - /** - * Host name prefix for secrets retrieval. This is added before the {@link #VAULTS_HOST_PREFIX}. - */ - String RETRIEVAL_HOST_PREFIX = "secrets"; - - /** - * Create a new fluent API builder for OCI metrics. - * - * @return a new builder - */ - static Builder builder() { - return new Builder(); - } - - /** - * Create OCI metrics using the default {@link io.helidon.integrations.oci.connect.OciRestApi}. - * - * @return OCI metrics instance connecting based on {@code DEFAULT} profile - */ - static OciVaultRx create() { - return builder().build(); - } - - /** - * Create OCI metrics based on configuration. - * - * @param config configuration on the node of OCI configuration - * @return OCI metrics instance configured from the configuration - * @see OciVaultRx.Builder#config(io.helidon.config.Config) - */ - static OciVaultRx create(Config config) { - return builder().config(config).build(); - } - - /** - * Gets information about the specified secret. - * - * @param request get secret request - * @return future with secret response or exception - */ - Single> getSecret(GetSecret.Request request); - - /** - * Create a new secret. - * - * @param request create secret request - * @return future with create secret response or exception - */ - Single createSecret(CreateSecret.Request request); - - /** - * Gets information about the specified secret. - * - * @param request get secret bundle request - * @return future with response or error - */ - Single> getSecretBundle(GetSecretBundle.Request request); - - /** - * Schedules a secret deletion. - * - * @param request delete secret request - * @return future with response or error - */ - Single deleteSecret(DeleteSecret.Request request); - - /** - * Encrypt data. - * - * @param request encryption request - * @return future with encrypted data - */ - Single encrypt(Encrypt.Request request); - - /** - * Decrypt data. - * - * @param request decryption request - * @return future with decrypted data - */ - Single decrypt(Decrypt.Request request); - - /** - * Sign a message. - * - * @param request signature request - * @return signature response - */ - Single sign(Sign.Request request); - - /** - * Verify a message signature. - * - * @param request verification request - * @return verification response - */ - Single verify(Verify.Request request); - - /** - * Get key metadata. - * - * @param request get key request - * @return get key response - */ - Single> getKey(GetKey.Request request); - - /** - * Get Vault metadata. - * - * @param request get vault request - * @return get vault response - */ - Single> getVault(GetVault.Request request); - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.vault.OciVaultRx}. - */ - @Configured - class Builder implements io.helidon.common.Builder { - private final OciRestApi.Builder accessBuilder = OciRestApi.builder(); - - private String secretApiVersion = SECRET_API_VERSION; - private String secretBundleApiVersion = SECRET_BUNDLE_API_VERSION; - private String vaultPrefix = VAULTS_HOST_PREFIX; - private String retrievalPrefix = RETRIEVAL_HOST_PREFIX; - private String kmsPrefix = KMS_HOST_PREFIX; - private String vaultEndpointFormat = OCI_ENDPOINT_FORMAT; - private String kmsEndpointFormat = ENDPOINT_FORMAT; - private String retrievalEndpointFormat = OCI_ENDPOINT_FORMAT; - private String vaultEndpoint; - private String retrievalEndpoint; - private String kmsEndpoint; - private String cryptographicEndpoint; - private String managementEndpoint; - private OciRestApi restApi; - - private Builder() { - } - - @Override - public OciVaultRx build() { - if (restApi == null) { - restApi = accessBuilder.build(); - } - return new OciVaultRxImpl(this); - } - - /** - * Update from configuration. The configuration must be located on the {@code OCI} root configuration - * node. - * - * @param config configuration - * @return updated metrics builder - */ - public Builder config(Config config) { - accessBuilder.config(config); - config.get("vault.secret-api-version").asString().ifPresent(this::secretApiVersion); - config.get("vault.secret-bundle-api-version").asString().ifPresent(this::secretBundleApiVersion); - config.get("vault.cryptographic-endpoint").asString().ifPresent(this::cryptographicEndpoint); - config.get("vault.management-endpoint").asString().ifPresent(this::managementEndpoint); - config.get("vault.vault-endpoint").asString().ifPresent(this::vaultEndpoint); - config.get("vault.retrieval-endpoint").asString().ifPresent(this::retrievalEndpoint); - config.get("vault.kms-endpoint").asString().ifPresent(this::kmsEndpoint); - config.get("vault.endpoint-format").asString().ifPresent(this::vaultEndpointFormat); - config.get("vault.vault-prefix").asString().ifPresent(this::vaultPrefix); - config.get("vault.retrieval-prefix").asString().ifPresent(this::retrievalPrefix); - return this; - } - - /** - * Replace Vault host prefix. - * Changing host prefix may be a breaking change. This API is designed to work with - * {@link #VAULTS_HOST_PREFIX}. - * - * @param vaultPrefix prefix to use - * @return updated builder - */ - public Builder vaultPrefix(String vaultPrefix) { - this.vaultPrefix = vaultPrefix; - return this; - } - - /** - * Replace retrieval host prefix. - * Changing host prefix may be a breaking change. This API is designed to work with - * {@link #RETRIEVAL_HOST_PREFIX}. - * - * @param retrievalHostPrefix prefix to use - * @return updated builder - */ - public Builder retrievalPrefix(String retrievalHostPrefix) { - this.retrievalPrefix = retrievalHostPrefix; - return this; - } - - /** - * Vault endpoint. - * - * @param vaultEndpoint valut endpoint - * @return updated builder - */ - public Builder vaultEndpoint(String vaultEndpoint) { - this.vaultEndpoint = vaultEndpoint; - return this; - } - - /** - * Endpoint to retrieve secrets from. - * - * @param retrievalEndpoint endpoint - * @return updated builder - */ - public Builder retrievalEndpoint(String retrievalEndpoint) { - this.retrievalEndpoint = retrievalEndpoint; - return this; - } - - /** - * Replace KMS host prefix. - * Changing host prefix may be a breaking change. This API is designed to work with - * {@link #KMS_HOST_PREFIX}. - * - * @param kmsPrefix prefix to use - * @return updated builder - */ - public Builder kmsPrefix(String kmsPrefix) { - this.kmsPrefix = kmsPrefix; - return this; - } - - /** - * KMS endpoint. - * - * @param kmsEndpoint KMS endpoint - * @return updated builder - */ - public Builder kmsEndpoint(String kmsEndpoint) { - this.kmsEndpoint = kmsEndpoint; - return this; - } - - /** - * Endpoint format to use. - * Default is {@link #OCI_ENDPOINT_FORMAT}. - * - * @param endpointFormat endpoint format to use - * @return updated builder - */ - public Builder vaultEndpointFormat(String endpointFormat) { - this.vaultEndpointFormat = endpointFormat; - return this; - } - - /** - * Configure API version to use. - * API version is part of the request URI. Changing API version is potentially breaking, - * as this API is designed to work with a specific version (see {@link #SECRET_API_VERSION}). - * - * @param apiVersion version of the API to use - * @return updated builder - */ - @ConfiguredOption(key = "vault.secret-api-version") - public Builder secretApiVersion(String apiVersion) { - this.secretApiVersion = apiVersion; - return this; - } - - /** - * Configure bundle API version to use. - * API version is part of the request URI. Changing API version is potentially breaking, - * as this API is designed to work with a specific version (see {@link #SECRET_BUNDLE_API_VERSION}). - * - * @param apiVersion version of the API to use - * @return updated builder - */ - public Builder secretBundleApiVersion(String apiVersion) { - this.secretBundleApiVersion = apiVersion; - return this; - } - - /** - * Configure the cryptographic endpoint. - * - * @param address endpoint for crypto operations - * @return updated builder - */ - public Builder cryptographicEndpoint(String address) { - this.cryptographicEndpoint = address; - return this; - } - - /** - * Configure the management endpoint. - * - * @param managementEndpoint management endpoint - * @return updated builder - */ - public Builder managementEndpoint(String managementEndpoint) { - this.managementEndpoint = managementEndpoint; - return this; - } - - /** - * Configure REST API to use. - * - * @param restApi OCI rest API - * @return updated builder - */ - @ConfiguredOption(key = "scheme", type = String.class, value = "https", description = "Scheme to use to " - + "connect to cloud") - @ConfiguredOption(key = "domain", type = String.class, value = "oraclecloud.com", description = "Cloud domain") - @ConfiguredOption(key = "config.instance-principal.enabled", type = Boolean.class, value = "false", - description = "Instance principal can be used by VMs provided by Oracle cloud") - @ConfiguredOption(key = "config.resource-principal.enabled", type = Boolean.class, value = "false", - description = "Resource principal can be used in fn (functions)") - @ConfiguredOption(key = "config.oci-profile", type = OciConfigProfile.class, description = "OCI profile is either " - + "read automatically from the default location, or can be configured explicitly") - public Builder restApi(OciRestApi restApi) { - this.restApi = restApi; - return this; - } - - /** - * Update the rest access builder to modify defaults. - * - * @param builderConsumer consumer of the builder - * @return updated metrics builder - */ - public Builder updateRestApi(Consumer builderConsumer) { - builderConsumer.accept(accessBuilder); - return this; - } - - String secretApiVersion() { - return secretApiVersion; - } - - String secretBundleApiVersion() { - return secretBundleApiVersion; - } - - OciRestApi restApi() { - return restApi; - } - - String cryptographicEndpoint() { - return cryptographicEndpoint; - } - - String managementEndpoint() { - return managementEndpoint; - } - - String vaultPrefix() { - return vaultPrefix; - } - - String retrievalPrefix() { - return retrievalPrefix; - } - - String kmsPrefix() { - return kmsPrefix; - } - - String vaultEndpointFormat() { - return vaultEndpointFormat; - } - - String kmsEndpointFormat() { - return kmsEndpointFormat; - } - - String retrievalEndpointFormat() { - return retrievalEndpointFormat; - } - - String vaultEndpoint() { - return vaultEndpoint; - } - - String retrievalEndpoint() { - return retrievalEndpoint; - } - - String kmsEndpoint() { - return kmsEndpoint; - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRxImpl.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRxImpl.java deleted file mode 100644 index 26eec8e1c92..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultRxImpl.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.Optional; -import java.util.function.Function; - -import io.helidon.common.configurable.LruCache; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Single; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; -import io.helidon.integrations.oci.connect.OciRestApi; - -import jakarta.json.JsonObject; - -class OciVaultRxImpl implements OciVaultRx { - private final LruCache keyIdToEndpointCache = LruCache.builder() - .capacity(100) - .build(); - - private final OciRestApi restApi; - private final String secretApiVersion; - private final String bundleApiVersion; - private final String vaultPrefix; - private final String retrievalPrefix; - private final String kmsPrefix; - private final Optional cryptographicEndpoint; - private final Optional managementEndpoint; - private final Optional vaultEndpoint; - private final Optional kmsEndpoint; - private final Optional retrievalEndpoint; - private final String vaultEndpointFormat; - private final String kmsEndpointFormat; - private final String retrievalEndpointFormat; - - OciVaultRxImpl(Builder builder) { - this.restApi = builder.restApi(); - this.secretApiVersion = builder.secretApiVersion(); - this.bundleApiVersion = builder.secretBundleApiVersion(); - this.vaultPrefix = builder.vaultPrefix(); - this.retrievalPrefix = builder.retrievalPrefix(); - this.kmsPrefix = builder.kmsPrefix(); - this.vaultEndpoint = Optional.ofNullable(builder.vaultEndpoint()); - this.kmsEndpoint = Optional.ofNullable(builder.kmsEndpoint()); - this.retrievalEndpoint = Optional.ofNullable(builder.retrievalEndpoint()); - this.cryptographicEndpoint = Optional.ofNullable(builder.cryptographicEndpoint()); - this.managementEndpoint = Optional.ofNullable(builder.managementEndpoint()); - this.vaultEndpointFormat = builder.vaultEndpointFormat(); - this.kmsEndpointFormat = builder.kmsEndpointFormat(); - this.retrievalEndpointFormat = builder.retrievalEndpointFormat(); - } - - @Override - public Single> getSecret(GetSecret.Request request) { - String apiPath = secretApiVersion + "/secrets/" + request.secretId(); - - vault(request); - - return restApi.get(apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(Secret::create)); - } - - @Override - public Single createSecret(CreateSecret.Request request) { - String apiPath = secretApiVersion + "/secrets"; - - vault(request); - - return restApi.invokeWithResponse(Http.Method.POST, - apiPath, - request, - CreateSecret.Response.builder()); - } - - @Override - public Single> getSecretBundle(GetSecretBundle.Request request) { - String apiPath = bundleApiVersion + "/secretbundles/" + request.secretId(); - - retrieval(request); - - return restApi.get(apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(GetSecretBundle.Response::create)); - } - - @Override - public Single deleteSecret(DeleteSecret.Request request) { - String apiPath = secretApiVersion + "/secrets/" + request.secretId() + "/actions/scheduleDeletion"; - - vault(request); - - return restApi.post(apiPath, - request, - DeleteSecret.Response.builder()); - } - - @Override - public Single encrypt(Encrypt.Request request) { - String apiPath = secretApiVersion + "/encrypt"; - - return cryptoEndpoint(request, request.keyId()) - .flatMapSingle(it -> restApi.invokeWithResponse(Http.Method.POST, - apiPath, - it, - Encrypt.Response.builder())); - } - - @Override - public Single decrypt(Decrypt.Request request) { - String apiPath = secretApiVersion + "/decrypt"; - - return cryptoEndpoint(request, request.keyId()) - .flatMapSingle(it -> restApi.invokeWithResponse(Http.Method.POST, - apiPath, - it, - Decrypt.Response.builder())); - } - - @Override - public Single sign(Sign.Request request) { - String apiPath = secretApiVersion + "/sign"; - - return cryptoEndpoint(request, request.keyId()) - .flatMapSingle(it -> restApi.invokeWithResponse(Http.Method.POST, - apiPath, - it, - Sign.Response.builder())); - } - - @Override - public Single verify(Verify.Request request) { - String apiPath = secretApiVersion + "/verify"; - - return cryptoEndpoint(request, request.keyId()) - .flatMapSingle(it -> restApi.invokeWithResponse(Http.Method.POST, - apiPath, - it, - Verify.Response.builder())); - } - - @Override - public Single> getKey(GetKey.Request request) { - String apiPath = secretApiVersion + "/keys/" + request.keyId(); - - managementEndpoint.ifPresentOrElse(request::endpoint, () -> { - throw new OciApiException("Management endpoint must be configured for key operations"); - }); - - return restApi.get(apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(GetKey.Response::create)); - } - - @Override - public Single> getVault(GetVault.Request request) { - String apiPath = secretApiVersion + "/vaults/" + request.vaultId(); - - kms(request); - - return restApi.get(apiPath, - request, - ApiOptionalResponse.apiResponseBuilder() - .entityProcessor(GetVault.Response::create)); - } - - private > Single cryptoEndpoint(T request, String keyId) { - if (request.endpoint().isPresent()) { - return Single.just(request); - } - return cryptographicEndpoint.map(Single::just) - .orElseGet(() -> keyIdToEndpointCache.get(keyId) - .map(Single::just) - .orElseGet(() -> getKey(GetKey.Request.create(keyId)) - .flatMapSingle(mapIfPresent("Could not locate key " + keyId)) - .map(GetKey.Response::vaultId) - .flatMapSingle(vaultId -> getVault(GetVault.Request.create(vaultId)) - .flatMapSingle(mapIfPresent("Could not locate vault " + vaultId))) - .map(GetVault.Response::cryptoEndpoint) - .peek(cryptoEndpoint -> keyIdToEndpointCache.put(keyId, cryptoEndpoint)))) - .map(request::endpoint); - } - - private Function, Single> mapIfPresent(String errorMessage) { - return apiOptional -> { - Optional entity = apiOptional.entity(); - - return entity.map(Single::just) - .orElseGet(() -> Single.error(new OciApiException(errorMessage))); - }; - } - - private void kms(OciRequestBase request) { - kmsEndpoint.ifPresent(request::endpoint); - request.hostPrefix(kmsPrefix); - request.hostFormat(kmsEndpointFormat); - } - - private void vault(OciRequestBase request) { - vaultEndpoint.ifPresent(request::endpoint); - request.hostPrefix(vaultPrefix); - request.hostFormat(vaultEndpointFormat); - } - - private void retrieval(OciRequestBase request) { - retrievalEndpoint.ifPresent(request::endpoint); - request.hostPrefix(retrievalPrefix + "." + vaultPrefix); - request.hostFormat(retrievalEndpointFormat); - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityProvider.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityProvider.java deleted file mode 100644 index e8c6a34ca9a..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityProvider.java +++ /dev/null @@ -1,672 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; - -import io.helidon.common.Base64Value; -import io.helidon.common.reactive.Single; -import io.helidon.config.Config; -import io.helidon.config.metadata.Configured; -import io.helidon.config.metadata.ConfiguredOption; -import io.helidon.integrations.common.rest.ApiOptionalResponse; -import io.helidon.security.SecretsProviderConfig; -import io.helidon.security.spi.DigestProvider; -import io.helidon.security.spi.EncryptionProvider; -import io.helidon.security.spi.ProviderConfig; -import io.helidon.security.spi.SecretsProvider; -import io.helidon.security.spi.SecurityProvider; - -/** - * Security provider to retrieve secrets from OCI Vault, and to use OCI KMS for encryption, - * decryption and signatures. - */ -@Configured(prefix = "oci-vault", - description = "OCI Vault Security Provider for secrets, encryption and digests", - provides = {SecretsProvider.class, EncryptionProvider.class, DigestProvider.class, SecurityProvider.class}) -public class OciVaultSecurityProvider implements SecretsProvider, - EncryptionProvider, - DigestProvider { - private final OciVaultRx ociVault; - - OciVaultSecurityProvider(OciVaultRx ociVault) { - this.ociVault = ociVault; - } - - /** - * Create a new instance from configuration. - * - * @param config configuration with connectivity to OCI - * @return a new provider instance - */ - @ConfiguredOption(mergeWithParent = true, - type = OciVaultRx.class, - description = "OCI Connectivity to Vault") - public static OciVaultSecurityProvider create(Config config) { - return new OciVaultSecurityProvider(OciVaultRx.create(config)); - } - - @Override - public Supplier>> secret(Config config) { - return secret(OciVaultSecretConfig.create(config)); - } - - @Override - public Supplier>> secret(OciVaultSecretConfig providerConfig) { - return () -> ociVault.getSecretBundle(providerConfig.request()) - .map(ApiOptionalResponse::entity) - .map(it -> it.flatMap(GetSecretBundle.Response::secretString)); - } - - @Override - public EncryptionSupport encryption(Config config) { - return encryption(OciVaultEncryptionConfig.create(config)); - } - - @Override - public EncryptionSupport encryption(OciVaultEncryptionConfig providerConfig) { - Function> encrypt = bytes -> ociVault.encrypt(providerConfig.encryptionRequest() - .data(Base64Value.create(bytes))) - .map(Encrypt.Response::cipherText); - - Function> decrypt = encrypted -> ociVault.decrypt(providerConfig.decryptionRequest() - .cipherText(encrypted)) - .map(Decrypt.Response::decrypted) - .map(Base64Value::toBytes); - - return EncryptionSupport.create(encrypt, decrypt); - } - - @Override - public DigestSupport digest(Config config) { - return digest(OciVaultDigestConfig.create(config)); - } - - @Override - public DigestSupport digest(OciVaultDigestConfig providerConfig) { - DigestFunction digestFunction = (data, preHashed) -> { - Sign.Request request = providerConfig.signRequest() - .message(Base64Value.create(data)) - .messageType(preHashed ? Sign.Request.MESSAGE_TYPE_DIGEST : Sign.Request.MESSAGE_TYPE_RAW); - - return ociVault.sign(request) - .map(Sign.Response::signature) - .map(Base64Value::toBase64); - }; - - VerifyFunction verifyFunction = (data, preHashed, digest) -> { - Verify.Request verifyRequest = providerConfig.verifyRequest() - .message(Base64Value.create(data)) - .messageType(preHashed ? Sign.Request.MESSAGE_TYPE_DIGEST : Sign.Request.MESSAGE_TYPE_RAW) - .signature(Base64Value.createFromEncoded(digest)); - - return ociVault.verify(verifyRequest) - .map(Verify.Response::isValid); - }; - - return DigestSupport.create(digestFunction, verifyFunction); - } - - /** - * Configuration for a signature. - */ - public static class OciVaultDigestConfig implements ProviderConfig { - private final String keyOcid; - private final String algorithm; - private final Optional keyVersionOcid; - private final Optional cryptographicEndpoint; - - private OciVaultDigestConfig(Builder builder) { - this.keyOcid = builder.keyOcid; - this.algorithm = builder.algorithm; - this.keyVersionOcid = Optional.ofNullable(builder.keyVersionOcid); - this.cryptographicEndpoint = Optional.ofNullable(builder.cryptographicEndpoint); - } - - /** - * Builder to set up configuration required to sign data using OCI KMS. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create a new configuration from config. - * - * @param config config - * @return a new instance - * @see Builder#config(io.helidon.config.Config) - */ - public static OciVaultDigestConfig create(Config config) { - return builder().config(config).build(); - } - - Sign.Request signRequest() { - Sign.Request request = Sign.Request.builder() - .keyId(keyOcid) - .algorithm(algorithm); - - keyVersionOcid.ifPresent(request::keyVersionId); - cryptographicEndpoint.ifPresent(request::endpoint); - - return request; - } - - Verify.Request verifyRequest() { - Verify.Request request = Verify.Request.builder() - .keyId(keyOcid) - .algorithm(algorithm); - - keyVersionOcid.ifPresent(request::keyVersionId); - cryptographicEndpoint.ifPresent(request::endpoint); - - return request; - } - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.vault.OciVaultSecurityProvider.OciVaultDigestConfig}. - */ - public static class Builder implements io.helidon.common.Builder { - private static final String CONFIG_KEY_KEY_OCID = "key-ocid"; - - private String keyOcid; - private String algorithm = Sign.Request.ALGORITHM_SHA_256_RSA_PKCS_PSS; - private String keyVersionOcid; - private String cryptographicEndpoint; - - private Builder() { - } - - @Override - public OciVaultDigestConfig build() { - Objects.requireNonNull(keyOcid, "Key ID must be defined for digest, configuration key \"" - + CONFIG_KEY_KEY_OCID + "\""); - - return new OciVaultDigestConfig(this); - } - - /** - * Update this builder from configuration. - * Configuration options: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Secret configuration
keydescriptionbuilder method
{@code key-ocid}OCID of the vault key to use for signatures, must be RSA{@link #keyId(String)}
{@code key-version-ocid}OCID of the key version{@link #keyVersionId(String)}
{@code algorithm}Signature algorithm{@link #algorithm(String)}
{@code cryptographic-endpoint}Cryptographic endpoint to use for signatures (available in console){@link #cryptographicEndpoint(String)}
- * - * @param config config to use - * @return updated builder - */ - public Builder config(Config config) { - config.get(CONFIG_KEY_KEY_OCID).asString().ifPresent(this::keyId); - config.get("key-version-ocid").asString().ifPresent(this::keyVersionId); - config.get("algorithm").asString().ifPresent(this::algorithm); - config.get("cryptographic-endpoint").asString().ifPresent(this::cryptographicEndpoint); - - return this; - } - - /** - * OCID of the key to use for signature. - * @param keyOcid OCID of the key - * @return updated builder - * @see Sign.Request#keyId(String) - */ - public Builder keyId(String keyOcid) { - this.keyOcid = keyOcid; - return this; - } - - /** - * Algorithm to sign with. - * - * @param algorithm algorithm - * @return updated builder - * @see Sign.Request#algorithm(String) - */ - public Builder algorithm(String algorithm) { - this.algorithm = algorithm; - return this; - } - - /** - * OCID of the key version. - * - * @param keyVersionOcid version OCID - * @return updated builder - * @see Sign.Request#keyVersionId(String) - */ - public Builder keyVersionId(String keyVersionOcid) { - this.keyVersionOcid = keyVersionOcid; - return this; - } - - /** - * Crypto endpoint to use. - * - * @param cryptographicEndpoint endpoint - * @return udpated builder - * @see io.helidon.integrations.oci.vault.OciVaultRx.Builder#cryptographicEndpoint(String) - */ - public Builder cryptographicEndpoint(String cryptographicEndpoint) { - this.cryptographicEndpoint = cryptographicEndpoint; - return this; - } - } - } - - /** - * Configuration for encryption/decryption. - */ - public static class OciVaultEncryptionConfig implements ProviderConfig { - private final String keyId; - private final Optional keyVersionId; - private final Optional algorithm; - private final Optional context; - private final Optional cryptographicEndpoint; - - private OciVaultEncryptionConfig(Builder builder) { - this.keyId = builder.keyId; - this.keyVersionId = Optional.ofNullable(builder.keyVersionId); - this.algorithm = Optional.ofNullable(builder.algorithm); - this.context = Optional.ofNullable(builder.context); - this.cryptographicEndpoint = Optional.ofNullable(builder.cryptographicEndpoint); - } - - /** - * A new builder for encryption configuration. - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create encryption configuration from config. - * - * @param config configuration - * @return a new encryption config - * @see Builder#config(io.helidon.config.Config) - */ - public static OciVaultEncryptionConfig create(Config config) { - return builder().config(config).build(); - } - - Encrypt.Request encryptionRequest() { - Encrypt.Request builder = Encrypt.Request.builder() - .keyId(keyId); - - keyVersionId.ifPresent(builder::keyVersionId); - algorithm.ifPresent(builder::algorithm); - context.ifPresent(builder::context); - cryptographicEndpoint.ifPresent(builder::endpoint); - - return builder; - } - - Decrypt.Request decryptionRequest() { - Decrypt.Request builder = Decrypt.Request.builder() - .keyId(keyId); - - keyVersionId.ifPresent(builder::keyVersionId); - algorithm.ifPresent(builder::algorithm); - context.ifPresent(builder::context); - cryptographicEndpoint.ifPresent(builder::endpoint); - - return builder; - } - - /** - * Fluent API builder for {@link io.helidon.integrations.oci.vault.OciVaultSecurityProvider.OciVaultEncryptionConfig}. - */ - public static class Builder implements io.helidon.common.Builder { - private static final String CONFIG_KEY_KEY_ID = "key-ocid"; - - private String keyId; - private String keyVersionId; - private String algorithm; - private String context; - private String cryptographicEndpoint; - - private Builder() { - } - - @Override - public OciVaultEncryptionConfig build() { - Objects.requireNonNull(keyId, "Key ID must be defined for encryption, configuration key \"" - + CONFIG_KEY_KEY_ID + "\""); - - return new OciVaultEncryptionConfig(this); - } - - /** - * Update this builder from configuration. - * Configuration options: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Secret configuration
keydescriptionbuilder method
{@code key-ocid}OCID of the vault key to use for encryption{@link #keyId(String)}
{@code key-version-ocid}OCID of the key version{@link #keyVersionId(String)}
{@code algorithm}Encryption algorithm{@link #algorithm(String)}
{@code cryptographic-endpoint}Cryptographic endpoint to use for encryption (available in console){@link #cryptographicEndpoint(String)}
{@code context}Contextual data{@link #context(String)}
- * - * @param config config to use - * @return updated builder - */ - public Builder config(Config config) { - config.get(CONFIG_KEY_KEY_ID).asString().ifPresent(this::keyId); - config.get("key-version-ocid").asString().ifPresent(this::keyVersionId); - config.get("algorithm").asString().ifPresent(this::algorithm); - config.get("context").asString().ifPresent(this::context); - config.get("cryptographic-endpoint").asString().ifPresent(this::cryptographicEndpoint); - return this; - } - - /** - * Configure the cryptographic endpoint to use. - * - * @param endpoint crypto endpoint - * @return updated builder - */ - public Builder cryptographicEndpoint(String endpoint) { - this.cryptographicEndpoint = endpoint; - return this; - } - - /** - * OCID of the key to use for encryption. - * - * @param keyId OCID of the key - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.Encrypt.Request#keyId(String) - * @see io.helidon.integrations.oci.vault.Decrypt.Request#keyId(String) - */ - public Builder keyId(String keyId) { - this.keyId = keyId; - return this; - } - - /** - * OCID of the key version. - * - * @param keyVersionId version OCID - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.Encrypt.Request#keyVersionId(String) - * @see io.helidon.integrations.oci.vault.Decrypt.Request#keyVersionId(String) - */ - public Builder keyVersionId(String keyVersionId) { - this.keyVersionId = keyVersionId; - return this; - } - - /** - * Algorithm to use for encryption. - * - * @param algorithm algorithm - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.Encrypt.Request#algorithm(String) - * @see io.helidon.integrations.oci.vault.Decrypt.Request#algorithm(String) - */ - public Builder algorithm(String algorithm) { - this.algorithm = algorithm; - return this; - } - - /** - * Contextual data. - * - * @param context context - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.Encrypt.Request#context(String) - * @see io.helidon.integrations.oci.vault.Decrypt.Request#context(String) - */ - public Builder context(String context) { - this.context = context; - return this; - } - } - } - - /** - * Configuration of an OCI Vault secret. - */ - public static class OciVaultSecretConfig implements SecretsProviderConfig { - private final String secretId; - private final Optional stage; - private final Optional versionName; - private final Optional versionNumber; - - private OciVaultSecretConfig(Builder builder) { - this.secretId = builder.secretId; - this.stage = Optional.ofNullable(builder.stage); - this.versionName = Optional.ofNullable(builder.versionName); - this.versionNumber = Optional.ofNullable(builder.versionNumber); - } - - /** - * A new builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Create secret configuration from config. - * - * @param config config - * @return a new secret configuration - */ - public static OciVaultSecretConfig create(Config config) { - return builder().config(config).build(); - } - - GetSecretBundle.Request request() { - GetSecretBundle.Request request = GetSecretBundle.Request.builder() - .secretId(secretId); - - stage.ifPresent(request::stage); - versionName.ifPresent(request::versionName); - versionNumber.ifPresent(request::versionNumber); - - return request; - } - - /** - * Fluent API builder for - * {@link io.helidon.integrations.oci.vault.OciVaultSecurityProvider.OciVaultSecretConfig}. - */ - @Configured(description = "Secrets retrieved from OCI Vault instance", provides = SecretsProviderConfig.class) - public static class Builder implements io.helidon.common.Builder { - private static final String SECRET_OCID_CONFIG_KEY = "ocid"; - - private String secretId; - private SecretStage stage; - private String versionName; - private Integer versionNumber; - - private Builder() { - } - - @Override - public OciVaultSecretConfig build() { - Objects.requireNonNull(secretId, "Secret OCID must be defined. Configuration key \"" - + SECRET_OCID_CONFIG_KEY + "\""); - return new OciVaultSecretConfig(this); - } - - /** - * Update this builder from configuration. - * Configuration options: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Secret configuration
keydescriptionbuilder method
{@code ocid}OCID of the secret{@link #secretId(String)}
{@code stage}Stage of the secret{@link #stage(SecretStage)}
{@code version-name}Name of the secret version{@link #versionName(String)}
{@code version-number}Version of the secret{@link #versionNumber(Integer)}
- * - * @param config config to use - * @return updated builder - */ - public Builder config(Config config) { - config.get(SECRET_OCID_CONFIG_KEY).asString().ifPresent(this::secretId); - config.get("stage").asString().map(SecretStage::valueOf).ifPresent(this::stage); - config.get("version-name").asString().ifPresent(this::versionName); - config.get("version-number").asInt().ifPresent(this::versionNumber); - - return this; - } - - /** - * Secret OCID. - * - * @param secretId secret OCID - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.GetSecretBundle.Request#secretId(String) - */ - @ConfiguredOption(key = "ocid", required = true) - public Builder secretId(String secretId) { - this.secretId = secretId; - return this; - } - - /** - * Secret stage. - * - * @param stage stage - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.GetSecretBundle.Request#stage(SecretStage) - */ - @ConfiguredOption - public Builder stage(SecretStage stage) { - this.stage = stage; - return this; - } - - /** - * Secret version name. - * - * @param versionName version name - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.GetSecretBundle.Request#versionName(String) - */ - @ConfiguredOption - public Builder versionName(String versionName) { - this.versionName = versionName; - return this; - } - - /** - * Secret version number. - * - * @param versionNumber version number - * @return updated builder - * - * @see io.helidon.integrations.oci.vault.GetSecretBundle.Request#versionNumber(int) - */ - @ConfiguredOption - public Builder versionNumber(Integer versionNumber) { - this.versionNumber = versionNumber; - return this; - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityService.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityService.java deleted file mode 100644 index 75cfedd61e8..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/OciVaultSecurityService.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.config.Config; -import io.helidon.security.spi.SecurityProvider; -import io.helidon.security.spi.SecurityProviderService; - -/** - * Service provider for {@link io.helidon.security.spi.SecurityProviderService}. - * Only used by a service loader. - * @deprecated do not use directly - */ -@Deprecated -public class OciVaultSecurityService implements SecurityProviderService { - /** - * This constructor is only intended for service loader. - * DO NOT USE DIRECTLY. - * @deprecated do not use - */ - @Deprecated - public OciVaultSecurityService() { - } - - @Override - public String providerConfigKey() { - return "oci-vault"; - } - - @Override - public Class providerClass() { - return OciVaultSecurityProvider.class; - } - - @Override - public SecurityProvider providerInstance(Config config) { - return OciVaultSecurityProvider.create(config); - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Secret.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Secret.java deleted file mode 100644 index 59ebed16055..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Secret.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import java.time.Instant; -import java.util.Optional; - -import io.helidon.integrations.oci.connect.OciResponseParser; - -import jakarta.json.JsonObject; - -/** - * A secret obtained from the vault. This object does not contain the actual secret content, - * please see - * {@link OciVaultRx#getSecretBundle(io.helidon.integrations.oci.vault.GetSecretBundle.Request)} - * to obtain secret content. - */ -public class Secret extends OciResponseParser { - private final String compartmentId; - private final String id; - private final String lifecycleState; - private final String name; - private final Instant created; - private final String vaultId; - private final Optional currentVersionNumber; - private final Optional description; - private final Optional keyId; - private final Optional lifecycleDetail; - private final Optional versionExpires; - private final Optional deleted; - - private Secret(JsonObject json) { - this.compartmentId = json.getString("compartmentId"); - this.id = json.getString("id"); - this.lifecycleState = json.getString("lifecycleState"); - this.name = json.getString("secretName"); - this.created = getInstant(json, "timeCreated"); - this.vaultId = json.getString("vaultId"); - this.currentVersionNumber = toInt(json, "currentVersionNumber"); - this.description = toString(json, "description"); - this.keyId = toString(json, "keyId"); - this.lifecycleDetail = toString(json, "lifecycleDetails"); - this.versionExpires = toInstant(json, "timeOfCurrentVersionExpiry"); - this.deleted = toInstant(json, "timeOfDeletion"); - } - - static Secret create(JsonObject json) { - return new Secret(json); - } - - /** - * The OCID of the compartment where the secret was created. - * - * @return compartment OCID - */ - public String compartmentId() { - return compartmentId; - } - - /** - * The OCID of the secret. - * - * @return secret OCID - */ - public String id() { - return id; - } - - /** - * The current lifecycle state of the secret. - * - * @return lifecycle state - */ - public String lifecycleState() { - return lifecycleState; - } - - /** - * The user-friendly name of the secret. Avoid entering confidential information. - * - * @return name - */ - public String name() { - return name; - } - - /** - * A property indicating when the secret was created. - * - * @return creation instant - */ - public Instant created() { - return created; - } - - /** - * The OCID of the vault where the secret exists. - * - * @return vault OCID - */ - public String vaultId() { - return vaultId; - } - - /** - * The version number of the secret version that's currently in use. - * - * @return version number - */ - public Optional currentVersionNumber() { - return currentVersionNumber; - } - - /** - * A brief description of the secret. Avoid entering confidential information. - * - * @return description - */ - public Optional description() { - return description; - } - - /** - * The OCID of the master encryption key that is used to encrypt the secret. - * - * @return key OCID - */ - public Optional keyId() { - return keyId; - } - - /** - * Additional information about the current lifecycle state of the secret. - * - * @return lifecycle information - */ - public Optional lifecycleDetail() { - return lifecycleDetail; - } - - /** - * An optional property indicating when the current secret version will expire. - * - * @return when the version expires - */ - public Optional versionExpires() { - return versionExpires; - } - - /** - * An optional property indicating when to delete the secret. - * - * @return deletion instant - */ - public Optional deleted() { - return deleted; - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/SecretStage.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/SecretStage.java deleted file mode 100644 index 63c9a9b4ab9..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/SecretStage.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -/** - * The rotation state of the secret version. - */ -public enum SecretStage { - /** - * Current value. - */ - CURRENT, - /** - * Pending. - */ - PENDING, - /** - * Latest. - */ - LATEST, - /** - * Previous. - */ - PREVIOUS, - /** - * Deprecated. - */ - DEPRECATED -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Sign.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Sign.java deleted file mode 100644 index 9c6235f074f..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Sign.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.common.Base64Value; -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonObject; - -/** - * Sign request and response. - */ -public final class Sign { - private Sign() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_224_RSA_PKCS_PSS = "SHA_224_RSA_PKCS_PSS"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_256_RSA_PKCS_PSS = "SHA_256_RSA_PKCS_PSS"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_384_RSA_PKCS_PSS = "SHA_384_RSA_PKCS_PSS"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_512_RSA_PKCS_PSS = "SHA_512_RSA_PKCS_PSS"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_224_RSA_PKCS1_V1_5 = "SHA_224_RSA_PKCS1_V1_5"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_256_RSA_PKCS1_V1_5 = "SHA_256_RSA_PKCS1_V1_5"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_384_RSA_PKCS1_V1_5 = "SHA_384_RSA_PKCS1_V1_5"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_SHA_512_RSA_PKCS1_V1_5 = "SHA_512_RSA_PKCS1_V1_5"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_ECDSA_SHA_256 = "ECDSA_SHA_256"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_ECDSA_SHA_384 = "ECDSA_SHA_384"; - /** - * {@value} algorithm. - */ - public static final String ALGORITHM_ECDSA_SHA_512 = "ECDSA_SHA_512"; - /** - * Raw message. - */ - public static final String MESSAGE_TYPE_RAW = "RAW"; - /** - * Digest of a message. - */ - public static final String MESSAGE_TYPE_DIGEST = "DIGEST"; - private String keyId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The base64-encoded binary data object denoting the message or message digest to sign. You can have a message up to - * 4096 bytes in size. To sign a larger message, provide the message digest. - * - * @param value value to sign - * @return updated request - * @see Base64Value#create(String) - * @see Base64Value#create(byte[]) - */ - public Request message(Base64Value value) { - return add("message", value.toBase64()); - } - - /** - * The OCID of the key to sign with. - * Required. - * - * @param keyOcid OCID of the key - * @return updated request - */ - public Request keyId(String keyOcid) { - this.keyId = keyOcid; - return add("keyId", keyOcid); - } - - /** - * Denotes whether the value of the message parameter is a raw message or a message digest. The default value, RAW, - * indicates a message. To indicate a message digest, use {@value #MESSAGE_TYPE_DIGEST}. - * - * @param type type to use - * @return updated request - * @see #MESSAGE_TYPE_DIGEST - * @see #MESSAGE_TYPE_RAW - */ - public Request messageType(String type) { - return add("messageType", type); - } - - /** - * The algorithm to use to sign the message or message digest. For RSA keys, supported signature schemes include PKCS - * #1 and RSASSA-PSS, along with different hashing algorithms. For ECDSA keys, ECDSA is the supported signature scheme - * with different hashing algorithms. When you pass a message digest for signing, ensure that you specify the same - * hashing algorithm as used when creating the message digest. - * Required. - * See algorithm constants on this class. - * - * @param algorithm algorithm to use - * @return updated request - */ - public Request algorithm(String algorithm) { - return add("signingAlgorithm", algorithm); - } - - /** - * The OCID of the key version used to sing the message. - * Optional. - * - * @param versionOcid OCID of the key version - * @return updated request - */ - public Request keyVersionId(String versionOcid) { - return add("keyVersionId", versionOcid); - } - - String keyId() { - if (keyId == null) { - throw new OciApiException("Encrypt.Request keyId must be defined"); - } - return keyId; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static final class Response extends ApiEntityResponse { - private final Base64Value signature; - private final String keyId; - private final String keyVersionId; - private final String signingAlgorithm; - - private Response(Builder builder) { - super(builder); - - JsonObject json = builder.entity(); - this.signature = Base64Value.createFromEncoded(json.getString("signature")); - this.keyId = json.getString("keyId"); - this.keyVersionId = json.getString("keyVersionId"); - this.signingAlgorithm = json.getString("signingAlgorithm"); - } - - static Builder builder() { - return new Builder(); - } - - /** - * The base64-encoded binary data object denoting the cryptographic signature generated for the message or message digest. - * - * @return signature - */ - public Base64Value signature() { - return signature; - } - - /** - * The OCID of the key used to sign the message. - * - * @return key id - */ - public String keyId() { - return keyId; - } - - /** - * The OCID of the key version used to sign the message. - * - * @return key version id - */ - public String keyVersionId() { - return keyVersionId; - } - - /** - * The algorithm to use to sign the message or message digest. For RSA keys, supported signature schemes include PKCS - * #1 and RSASSA-PSS, along with different hashing algorithms. For ECDSA keys, ECDSA is the supported signature scheme - * with different hashing algorithms. When you pass a message digest for signing, ensure that you specify the same - * hashing algorithm as used when creating the message digest. - * - * @return algorithm - */ - public String signingAlgorithm() { - return signingAlgorithm; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Verify.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Verify.java deleted file mode 100644 index 57b4f6c8b71..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/Verify.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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. - */ - -package io.helidon.integrations.oci.vault; - -import io.helidon.common.Base64Value; -import io.helidon.integrations.common.rest.ApiEntityResponse; -import io.helidon.integrations.oci.connect.OciApiException; -import io.helidon.integrations.oci.connect.OciRequestBase; - -import jakarta.json.JsonObject; - -/** - * Sign request and response. - */ -public final class Verify { - private Verify() { - } - - /** - * Request object. Can be configured with additional headers, query parameters etc. - */ - public static final class Request extends OciRequestBase { - private String keyId; - - private Request() { - } - - /** - * Fluent API builder for configuring a request. - * The request builder is passed as is, without a build method. - * The equivalent of a build method is {@link #toJson(jakarta.json.JsonBuilderFactory)} - * used by the {@link io.helidon.integrations.common.rest.RestApi}. - * - * @return new request builder - */ - public static Request builder() { - return new Request(); - } - - /** - * The base64-encoded binary data object denoting the message or message digest to sign. You can have a message up to - * 4096 bytes in size. To sign a larger message, provide the message digest. - * - * @param value value to verify - * @return updated request - * @see Base64Value#create(String) - * @see Base64Value#create(byte[]) - */ - public Request message(Base64Value value) { - return add("message", value.toBase64()); - } - - /** - * The base64-encoded binary data object denoting the cryptographic signature generated for the message. - * - * @param signature signature to verify - * @return updated request - */ - public Request signature(Base64Value signature) { - return add("signature", signature.toBase64()); - } - - /** - * The OCID of the key to sign with. - * Required. - * - * @param keyOcid OCID of the key - * @return updated request - */ - public Request keyId(String keyOcid) { - this.keyId = keyOcid; - return add("keyId", keyOcid); - } - - /** - * Denotes whether the value of the message parameter is a raw message or a message digest. The default value, RAW, - * indicates a message. To indicate a message digest, use {@value Sign.Request#MESSAGE_TYPE_DIGEST}. - * - * @param type type to use - * @return updated request - * @see Sign.Request#MESSAGE_TYPE_DIGEST - * @see Sign.Request#MESSAGE_TYPE_RAW - */ - public Request messageType(String type) { - return add("messageType", type); - } - - /** - * The algorithm to use to sign the message or message digest. For RSA keys, supported signature schemes include PKCS - * #1 and RSASSA-PSS, along with different hashing algorithms. For ECDSA keys, ECDSA is the supported signature scheme - * with different hashing algorithms. When you pass a message digest for signing, ensure that you specify the same - * hashing algorithm as used when creating the message digest. - * Required. - * See algorithm constants on {@link Sign.Request} class. - * - * @param algorithm algorithm to use - * @return updated request - */ - public Request algorithm(String algorithm) { - return add("signingAlgorithm", algorithm); - } - - /** - * The OCID of the key version used to sing the message. - * Optional. - * - * @param versionOcid OCID of the key version - * @return updated request - */ - public Request keyVersionId(String versionOcid) { - return add("keyVersionId", versionOcid); - } - - String keyId() { - if (keyId == null) { - throw new OciApiException("Encrypt.Request keyId must be defined"); - } - return keyId; - } - } - - /** - * Response object parsed from JSON returned by the {@link io.helidon.integrations.common.rest.RestApi}. - */ - public static final class Response extends ApiEntityResponse { - private final boolean valid; - - private Response(Builder builder) { - super(builder); - - JsonObject json = builder.entity(); - this.valid = json.getBoolean("isSignatureValid"); - } - - static Builder builder() { - return new Builder(); - } - - /** - * A Boolean value that indicates whether the signature was verified. - * - * @return whether the signature is valid - */ - public boolean isValid() { - return valid; - } - - static final class Builder extends ApiEntityResponse.Builder { - private Builder() { - } - - @Override - public Response build() { - return new Response(this); - } - } - } -} diff --git a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/package-info.java b/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/package-info.java deleted file mode 100644 index 6639110efd1..00000000000 --- a/integrations/oci/vault/src/main/java/io/helidon/integrations/oci/vault/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -/** - * Integration with OCI Vault REST API including the KMS encryption and digest support. - */ -package io.helidon.integrations.oci.vault; diff --git a/integrations/oci/vault/src/main/java/module-info.java b/integrations/oci/vault/src/main/java/module-info.java deleted file mode 100644 index 03128240726..00000000000 --- a/integrations/oci/vault/src/main/java/module-info.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -/** - * Integration with OCI Vault REST API including the KMS encryption and digest support. - */ -module io.helidon.integrations.oci.vault { - requires io.helidon.security; - requires io.helidon.common.reactive; - requires io.helidon.integrations.oci.connect; - requires jakarta.json; - requires io.helidon.common.http; - requires transitive io.helidon.integrations.common.rest; - requires static io.helidon.config.metadata; - - exports io.helidon.integrations.oci.vault; - - provides io.helidon.security.spi.SecurityProviderService - with io.helidon.integrations.oci.vault.OciVaultSecurityService; - - provides io.helidon.integrations.oci.connect.spi.InjectionProvider - with io.helidon.integrations.oci.vault.OciVaultInjectionProvider; - - opens io.helidon.integrations.oci.vault to weld.core.impl, io.helidon.microprofile.cdi; -} \ No newline at end of file diff --git a/integrations/oci/vault/src/main/resources/META-INF/helidon/native-image/weld-proxies.json b/integrations/oci/vault/src/main/resources/META-INF/helidon/native-image/weld-proxies.json deleted file mode 100644 index 125bda0d9b8..00000000000 --- a/integrations/oci/vault/src/main/resources/META-INF/helidon/native-image/weld-proxies.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.vault.OciVault" - ] - }, - { - "bean-class": "io.helidon.integrations.oci.cdi.OciCdiExtension", - "ifaces": [ - "io.helidon.integrations.oci.vault.OciVaultRx" - ] - } -] \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml index 01cec081730..e8d32476d71 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -115,6 +115,31 @@ david.k.kral@oracle.com Oracle Corporation + + Daniel Kec + daniel.kec@oracle.com + Oracle Corporation + + + Arjav Desai + arjav.desai@oracle.com + Oracle Corporation + + + Keith Lustria + keith.lustria@oracle.com + Oracle Corporation + + + Dmitry Aleksandrov + dmitry.aleksandrov@oracle.com + Oracle Corporation + + + Jeff Trent + jeff.trent@oracle.com + Oracle Corporation + diff --git a/pom.xml b/pom.xml index 7070f16fc49..70151feb972 100644 --- a/pom.xml +++ b/pom.xml @@ -1195,7 +1195,7 @@ false -helidon-parent,helidon-dependencies,helidon-bom,helidon-se,helidon-mp,io.grpc,helidon-mp-graal-native-image-extension,helidon-graal-native-image-extension,helidon-config-test-infrastructure,helidon-webserver-test-support,helidon-integrations-cdi-oci-objectstorage +helidon-parent,helidon-dependencies,helidon-bom,helidon-se,helidon-mp,io.grpc,helidon-mp-graal-native-image-extension,helidon-graal-native-image-extension,helidon-config-test-infrastructure,helidon-webserver-test-support io.grpc From dba79417924f30a021f3ef2529518efe69416d3e Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 14 Jul 2022 02:43:52 +0200 Subject: [PATCH 09/51] Attempted fix for intermittent issue + debugging info in case of failure. (#4532) Signed-off-by: Tomas Langer --- .../tests/it1/OpentraceableClientE2ETest.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java b/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java index 782822aeaa6..7b5f7a74c5d 100644 --- a/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java +++ b/tracing/tests/it-tracing-client-zipkin/src/test/java/io/helidon/tracing/tests/it1/OpentraceableClientE2ETest.java @@ -22,9 +22,7 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import io.helidon.tracing.Span; import io.helidon.tracing.jersey.client.ClientTracingFilter; @@ -42,11 +40,13 @@ import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; 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.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.fail; @@ -63,11 +63,29 @@ class OpentraceableClientE2ETest { private static Client client; @BeforeAll - static void startServerInitClient() throws Exception { + static void startServerInitClient() { server = startWebServer(); client = ClientBuilder.newClient(new ClientConfig(ClientTracingFilter.class)); } + @AfterAll + static void stopAndClose() throws Exception { + if (server != null) { + server.shutdown() + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); + } + + if (client != null) { + client.close(); + } + } + + @BeforeEach + void resetTraces() { + EVENTS_MAP.clear(); + } + @Test void e2e() throws Exception { io.helidon.tracing.Tracer tracer = tracer("test-client"); @@ -89,21 +107,13 @@ void e2e() throws Exception { TraceContext traceContext = ((BraveSpanContext) start.unwrap(io.opentracing.Span.class).context()).unwrap(); - assertSpanChain(EVENTS_MAP.remove(traceContext.traceIdString()), EVENTS_MAP); + zipkin2.Span reportedSpan = EVENTS_MAP.remove(traceContext.traceIdString()); + assertThat("Span with id " + reportedSpan.traceId() + " was not found in " + + printSpans(EVENTS_MAP), reportedSpan, notNullValue()); + assertSpanChain(reportedSpan, EVENTS_MAP); assertThat(EVENTS_MAP.entrySet(), hasSize(0)); } - @AfterEach - public void stopAndClose() throws Exception { - if (server != null) { - server.shutdown() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); - } - - client.close(); - } - /** * Use custom {@link Tracer} that adds events to {@link #EVENTS_MAP} map. */ @@ -111,7 +121,7 @@ private static io.helidon.tracing.Tracer tracer(String serviceName) { Tracing braveTracing = Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(span -> { - EVENTS_MAP.put(span.id(), span); + EVENTS_MAP.put(span.traceId(), span); EVENTS_LATCH.countDown(); }) .build(); @@ -120,7 +130,7 @@ private static io.helidon.tracing.Tracer tracer(String serviceName) { return OpenTracing.create(new ZipkinTracer(BraveTracer.create(braveTracing), List.of())); } - private static WebServer startWebServer() throws InterruptedException, ExecutionException, TimeoutException { + private static WebServer startWebServer() { return WebServer.builder() .host("localhost") .routing(Routing.builder() From 0b030554a2d91ab62a547fca126ab3ef9dbbd409 Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Wed, 13 Jul 2022 20:53:41 -0700 Subject: [PATCH 10/51] Archetypes updates (#4538) * Uptake build-tools 3.0.0-RC1 Archetypes templates implementation Fixes #4276 --- Jenkinsfile | 20 +- applications/pom.xml | 4 +- archetypes/helidon/pom.xml | 49 ++ .../src/main/archetype/common/common.xml | 14 +- .../src/main/archetype/common/docker.xml | 2 +- .../src/main/archetype/common/extra.xml | 234 ++++++++ .../archetype/common/files/.helidon.mustache | 2 +- .../archetype/common/files/app.yaml.mustache | 28 +- .../common/files/application.abac.yaml | 34 ++ .../common/files/application.google.yaml | 20 + .../files/application.http-signature.yaml | 22 + .../common/files/application.jwt.yaml | 14 + .../common/files/application.oidc.yaml | 15 + .../archetype/common/files/pom.xml.mustache | 5 +- .../files/src/main/resources/keystore.p12 | Bin 0 -> 2700 bytes .../main/archetype/common/media-sources.xml | 44 ++ .../src/main/archetype/common/media.xml | 171 ++++++ .../main/archetype/common/observability.xml | 561 ++++++++++++++++++ .../src/main/archetype/common/packaging.xml | 102 ++++ .../src/main/archetype/common/presets.xml | 41 ++ .../src/main/archetype/common/security.xml | 201 +++++++ .../{project-sources.xml => sources.xml} | 5 +- .../main/archetype/mp/common/common-mp.xml | 5 +- .../main/archetype/mp/common/files/README.md | 17 - .../microprofile-config.properties.mustache | 8 + .../main/resources/application.yaml.mustache | 13 + .../test/java/__pkg__/MainTest.java.mustache | 18 - .../test/resources/application.yaml.mustache | 2 + .../bare-mp.xml => custom/custom-mp.xml} | 54 +- .../src/main/archetype/mp/custom/database.xml | 435 ++++++++++++++ .../mp/{bare => custom}/files/README.md | 0 .../files/microprofile-config.properties | 0 .../FileService.java.multipart.mustache | 88 +++ .../FileStorage.java.multipart.mustache | 89 +++ .../java/__pkg__/FtResource.java.mustache | 57 ++ .../java/__pkg__/Message.java.json.mustache | 31 + ...artFeatureProvider.java.multipart.mustache | 19 + .../SimpleGreetResource.java.json.mustache | 57 ++ .../SimpleGreetResource.java.jsonp.mustache} | 20 +- .../FileServiceTest.java.multipart.mustache | 101 ++++ .../java/__pkg__/FtResourceTest.java.mustache | 60 ++ .../test/java/__pkg__/TestCORS.java.mustache | 140 +++++ .../archetype/mp/database/database-mp.xml | 215 +------ .../files/microprofile-config.properties | 6 - .../main/java/__pkg__/Pokemon.java.mustache | 2 - .../META-INF/persistence.xml.mustache | 13 +- ...operties => hibernate.properties.mustache} | 0 .../test/java/__pkg__/MainTest.java.mustache | 111 ---- .../META-INF/microprofile-config.properties | 16 + .../META-INF/persistence.xml.mustache | 39 ++ .../helidon/src/main/archetype/mp/mp.xml | 16 +- .../__pkg__/GreetResource.java.json.mustache | 115 ++++ ...ache => GreetResource.java.jsonp.mustache} | 0 .../archetype/mp/quickstart/quickstart-mp.xml | 36 +- .../src/main/archetype/se/bare/bare-se.xml | 85 --- .../main/archetype/se/common/common-se.xml | 24 +- .../main/archetype/se/common/files/README.md | 17 - .../src/main/java/__pkg__/Main.java.mustache | 18 +- .../main/resources/application.yaml.mustache | 14 + .../test/java/__pkg__/MainTest.java.mustache | 18 - .../test/resources/application.yaml.mustache | 13 + .../main/archetype/se/custom/custom-se.xml | 50 ++ .../main/archetype/se/custom/custom-tests.xml | 54 ++ .../src/main/archetype/se/custom/database.xml | 356 +++++++++++ .../se/{bare => custom}/files/README.md | 0 .../files/application.h2.yaml} | 13 +- .../se/custom/files/application.mongo.yaml | 58 ++ .../se/custom/files/application.mysql.yaml | 40 ++ .../se/custom/files/application.oracledb.yaml | 40 ++ .../java/__pkg__/CorsService.java.mustache | 25 + .../FileService.java.multipart.mustache | 86 +++ .../FileStorage.java.multipart.mustache | 97 +++ .../main/java/__pkg__/FtService.java.mustache | 152 +++++ .../java/__pkg__/Message.java.json.mustache | 28 + .../SimpleGreetService.java.json.mustache | 71 +++ .../SimpleGreetService.java.jsonp.mustache} | 23 +- .../java/__pkg__/WebClientMain.java.mustache | 52 ++ .../main/resources/application.config.yaml} | 0 .../FileServiceTest.java.multipart.mustache | 123 ++++ .../java/__pkg__/FtServiceTest.java.mustache | 184 ++++++ .../test/java/__pkg__/TestCORS.java.mustache | 150 +++++ .../__pkg__/WebClientMainTest.java.mustache | 43 ++ ...don.dbclient.spi.DbMapperProvider.mustache | 1 + .../archetype/se/database/database-se.xml | 176 +----- .../PokemonMapperProvider.java.mustache | 8 +- .../PokemonTypeMapperProvider.java.mustache | 9 +- .../__pkg__/GreetService.java.json.mustache | 112 ++++ ...tache => GreetService.java.jsonp.mustache} | 0 .../archetype/se/quickstart/quickstart-se.xml | 50 +- .../se/quickstart/quickstart-tests.xml | 89 +++ .../helidon/src/main/archetype/se/se.xml | 16 +- archetypes/legacy/bare-mp/pom.xml | 2 +- archetypes/legacy/bare-se/pom.xml | 2 +- archetypes/legacy/pom.xml | 2 +- etc/scripts/test-archetypes.sh | 37 ++ pom.xml | 2 +- 96 files changed, 4888 insertions(+), 823 deletions(-) create mode 100644 archetypes/helidon/src/main/archetype/common/extra.xml create mode 100644 archetypes/helidon/src/main/archetype/common/files/application.abac.yaml create mode 100644 archetypes/helidon/src/main/archetype/common/files/application.google.yaml create mode 100644 archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml create mode 100644 archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml create mode 100644 archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml create mode 100644 archetypes/helidon/src/main/archetype/common/files/src/main/resources/keystore.p12 create mode 100644 archetypes/helidon/src/main/archetype/common/media-sources.xml create mode 100644 archetypes/helidon/src/main/archetype/common/media.xml create mode 100644 archetypes/helidon/src/main/archetype/common/observability.xml create mode 100644 archetypes/helidon/src/main/archetype/common/packaging.xml create mode 100644 archetypes/helidon/src/main/archetype/common/presets.xml create mode 100644 archetypes/helidon/src/main/archetype/common/security.xml rename archetypes/helidon/src/main/archetype/common/{project-sources.xml => sources.xml} (91%) delete mode 100644 archetypes/helidon/src/main/archetype/mp/common/files/README.md create mode 100644 archetypes/helidon/src/main/archetype/mp/common/files/src/main/resources/application.yaml.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/common/files/src/test/resources/application.yaml.mustache rename archetypes/helidon/src/main/archetype/mp/{bare/bare-mp.xml => custom/custom-mp.xml} (55%) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/database.xml rename archetypes/helidon/src/main/archetype/mp/{bare => custom}/files/README.md (100%) rename archetypes/helidon/src/main/archetype/mp/{bare => custom}/files/microprofile-config.properties (100%) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FtResource.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/Message.java.json.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/MultiPartFeatureProvider.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.json.mustache rename archetypes/helidon/src/main/archetype/mp/{bare/files/src/main/java/__pkg__/GreetResource.java.mustache => custom/files/src/main/java/__pkg__/SimpleGreetResource.java.jsonp.mustache} (72%) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FtResourceTest.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/TestCORS.java.mustache delete mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/microprofile-config.properties rename archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/{hibernate.properties => hibernate.properties.mustache} (100%) delete mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/src/test/java/__pkg__/MainTest.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/microprofile-config.properties create mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/persistence.xml.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.json.mustache rename archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/{GreetResource.java.mustache => GreetResource.java.jsonp.mustache} (100%) delete mode 100644 archetypes/helidon/src/main/archetype/se/bare/bare-se.xml delete mode 100644 archetypes/helidon/src/main/archetype/se/common/files/README.md create mode 100644 archetypes/helidon/src/main/archetype/se/common/files/src/test/resources/application.yaml.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/custom-se.xml create mode 100644 archetypes/helidon/src/main/archetype/se/custom/custom-tests.xml create mode 100644 archetypes/helidon/src/main/archetype/se/custom/database.xml rename archetypes/helidon/src/main/archetype/se/{bare => custom}/files/README.md (100%) rename archetypes/helidon/src/main/archetype/se/{database/files/application.yaml => custom/files/application.h2.yaml} (73%) create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/application.mongo.yaml create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/application.mysql.yaml create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/application.oracledb.yaml create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/CorsService.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FtService.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache rename archetypes/helidon/src/main/archetype/se/{bare/files/src/main/java/__pkg__/GreetService.java.mustache => custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache} (73%) create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache rename archetypes/helidon/src/main/archetype/se/{bare/files/application.yaml => custom/files/src/main/resources/application.config.yaml} (100%) create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FtServiceTest.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/TestCORS.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/resources/META-INF/services/io.helidon.dbclient.spi.DbMapperProvider.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.json.mustache rename archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/{GreetService.java.mustache => GreetService.java.jsonp.mustache} (100%) create mode 100644 archetypes/helidon/src/main/archetype/se/quickstart/quickstart-tests.xml create mode 100755 etc/scripts/test-archetypes.sh diff --git a/Jenkinsfile b/Jenkinsfile index a8097874092..fbf40d5c92d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - pipeline { agent { label "linux" @@ -49,6 +48,21 @@ pipeline { sh './etc/scripts/checkstyle.sh' } } + stage('archetypes'){ + agent { + label "linux" + } + steps { + script { + try { + sh 'etc/scripts/test-archetypes.sh' + } finally { + archiveArtifacts artifacts: "archetypes/**/target/**/*.txt" + junit testResults: '**/target/surefire-reports/*.xml' + } + } + } + } stage('integration-tests') { stages { stage('test-vault') { @@ -109,4 +123,4 @@ pipeline { } } } -} +} \ No newline at end of file diff --git a/applications/pom.xml b/applications/pom.xml index faabd6470e1..1e118d23bca 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -47,8 +47,8 @@ 3.1.2 1.6.0 3.0.0-M5 - 3.0.0-M3 - 3.0.0-M3 + 3.0.0-RC1 + 3.0.0-RC1 3.0.2 1.5.0.Final 0.5.1 diff --git a/archetypes/helidon/pom.xml b/archetypes/helidon/pom.xml index 0e5a26f1934..1c5f236ae64 100644 --- a/archetypes/helidon/pom.xml +++ b/archetypes/helidon/pom.xml @@ -47,12 +47,61 @@ io.helidon.build-tools helidon-archetype-maven-plugin + false + + true + ${project.version} + + + + + + ${media} == 'multipart' || !(${media} contains 'multipart') + + !(${metrics} || ${tracing} || ${health}) || (${metrics} && ${tracing} && ${health}) + + !${health} || (${health.builtin}) + + !${metrics} || (${metrics.builtin}) + + !${tracing} || (${tracing} && ${metrics.provider} == 'microprofile') + + ${extra} == [] || (${extra} contains 'cors' && ${extra} contains 'webclient' && ${extra} contains 'fault-tolerance') + + !(${docker} || ${k8s} || ${v8o}) || (${docker} && ${k8s} && ${v8o}) + + !${docker} || (${docker.native-image} && ${docker.jlink-image}) + + ${security.atn} == 'oidc' || ${security.atn} == 'jwt' || ${security.atn} == 'google' || ${security.atn} == 'http-signature' + + (${security.atz} == 'abac' && ${security.atn} == 'oidc') || ${security.atz} == 'none' + + (${app-type} == 'custom' && ${security} == 'true' && ${media} == 'none') || ${security} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${metrics} == 'true' && ${media} == 'none') || ${metrics} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${metrics} == 'true' && ${security} == 'false') || ${metrics} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${docker} == 'true' && ${media} == 'none') || ${docker} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${docker} == 'true' && ${security} == 'false') || ${docker} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${docker} == 'true' && ${tracing} == 'false') || ${docker} == 'false' || ${app-type} != 'custom' + + (${app-type} == 'custom' && ${db} == 'false') || ${app-type} != 'custom' + + (${app-type} == 'database' && ${db.auto-ddl} == 'true') && ${media.json-lib} == 'jackson' || (${db.auto-ddl} == 'false' && ${media.json-lib} != 'jackson') || ${app-type} != 'database' + + (${app-type} == 'database' && ${db.cp} == 'hikaricp') && ${media.json-lib} == 'jackson' || (${db.cp} != 'hikaricp' && ${media.json-lib} != 'jackson' ) || ${app-type} != 'database' + + (${app-type} == 'database' && ${health} == 'false') || ${app-type} != 'database' + diff --git a/archetypes/helidon/src/main/archetype/common/common.xml b/archetypes/helidon/src/main/archetype/common/common.xml index e6bddcba00a..d857052bab7 100644 --- a/archetypes/helidon/src/main/archetype/common/common.xml +++ b/archetypes/helidon/src/main/archetype/common/common.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + true + true + true + + + + diff --git a/archetypes/helidon/src/main/archetype/common/files/.helidon.mustache b/archetypes/helidon/src/main/archetype/common/files/.helidon.mustache index 8a63fe3f29a..e4a1bbad842 100644 --- a/archetypes/helidon/src/main/archetype/common/files/.helidon.mustache +++ b/archetypes/helidon/src/main/archetype/common/files/.helidon.mustache @@ -3,4 +3,4 @@ schema.version={{dot-helidon-schema-version}} helidon.version={{helidon-version}} project.flavor={{flavor}} -project.archetype={{base}} +project.archetype={{app-type}} diff --git a/archetypes/helidon/src/main/archetype/common/files/app.yaml.mustache b/archetypes/helidon/src/main/archetype/common/files/app.yaml.mustache index 84472b9bd87..e684f72c9db 100644 --- a/archetypes/helidon/src/main/archetype/common/files/app.yaml.mustache +++ b/archetypes/helidon/src/main/archetype/common/files/app.yaml.mustache @@ -29,25 +29,11 @@ spec: targetPort: 8080 name: http --- -kind: Deployment -apiVersion: apps/v1 -metadata: - name: {{artifactId}} -spec: - replicas: 1 - selector: - matchLabels: - app: {{artifactId}} - template: - metadata: - labels: - app: {{artifactId}} - version: v1 - spec: - containers: - - name: {{artifactId}} - image: {{artifactId}} - imagePullPolicy: IfNotPresent - ports: - - containerPort: 8080 +{{#yaml-section}} +{{.}} --- +{{/yaml-section}} + +{{#app-yaml-content}} +{{.}} +{{/app-yaml-content}} \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml b/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml new file mode 100644 index 00000000000..e421b77e344 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml @@ -0,0 +1,34 @@ + - abac: + # prepares environment + # executes attribute validations + # validates that attributes were processed + # grants/denies access to resource + # + #### + # Combinations: + # # Will fail if any attribute is not validated and if any has failed validation + # fail-on-unvalidated: true + # fail-if-none-validated: true + # + # # Will fail if there is one or more attributes present and NONE of them is validated or if any has failed validation + # # Will NOT fail if there is at least one validated attribute and any number of not validated attributes (and NONE failed) + # fail-on-unvalidated: false + # fail-if-none-validated: true + # + # # Will fail if there is any attribute that failed validation + # # Will NOT fail if there are no failed validation or if there are NONE validated + # fail-on-unvalidated: false + # fail-if-none-validated: false + #### + # fail if an attribute was not validated (e.g. we do not know, whether it is valid or not) + # defaults to true + fail-on-unvalidated: true + # fail if none of the attributes were validated + # defaults to true + fail-if-none-validated: true +# policy-validator: +# validators: +# - class: "io.helidon.security.abac.policy.DefaultPolicyValidator" +# my-custom-policy-engine: +# some-key: "some value" +# another-key: "another value" diff --git a/archetypes/helidon/src/main/archetype/common/files/application.google.yaml b/archetypes/helidon/src/main/archetype/common/files/application.google.yaml new file mode 100644 index 00000000000..4edad1494e7 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/files/application.google.yaml @@ -0,0 +1,20 @@ + - google-login: + # Create your own application in Google developer console + # Also update the client id configured in header of index.html + # Detailed how-to for login button (including links how to create an application): + # https://developers.google.com/identity/sign-in/web/sign-in + client-id: "google-client-id" + # Defaults for Helidon + # realm: "helidon" + # Configure proxy host if needed + proxy-host: "proxy-host" + # proxy-port: 80 + + # This is the default for GoogleTokenProvider + #token: + # header: "Authorization" + # or do not specify - then the whole header is considered to be the token value + # prefix: "bearer " + # optional alternative - looking for first matching group + # regexp: "bearer (.*)" + #} diff --git a/archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml b/archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml new file mode 100644 index 00000000000..2f86e37e46e --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml @@ -0,0 +1,22 @@ + - http-signatures: + # only inbound configured, no outbound calls + inbound: + keys: + - key-id: "service1-hmac" + principal-name: "Service1 - HMAC signature" + # See [EncryptionFilter](https://helidon.io/docs/latest/apidocs/io.helidon.config.encryption/io/helidon/config/encryption/EncryptionFilter.html) for details about encrypting passwords in configuration files. + hmac.secret: "somePasswordForHmacShouldBeEncrypted" + - key-id: "service1-rsa" + principal-name: "Service1 - RSA signature" + public-key: + keystore: + # path to keystore + resource.path: "src/main/resources/keystore.p12" + # Keystore type + # PKCS12 or JKS + # defaults to jdk default + # keystore-type: "PKCS12" + # password of the keystore + passphrase: "password" + # alias of the certificate to get public key from + cert.alias: "service_cert" diff --git a/archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml b/archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml new file mode 100644 index 00000000000..46f5250f022 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml @@ -0,0 +1,14 @@ + - atn-token: + jwk.resource.resource-path: "verifying-jwk.json" + jwt-audience: "http://my.service" + - sign-token: + jwk.resource.resource-path: "signing-jwk.json" + jwt-issuer: "http://my.server/identity" + outbound: + - name: "propagate-token" + hosts: ["*.internal.org"] + - name: "generate-token" + hosts: ["1.partner-service"] + jwk-kid: "partner-1" + jwt-kid: "helidon" + jwt-audience: "http://1.partner-service" diff --git a/archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml b/archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml new file mode 100644 index 00000000000..cfd8a1b9e3a --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml @@ -0,0 +1,15 @@ + - oidc: + client-id: "client-id-of-this-service" + # See [EncryptionFilter](https://helidon.io/docs/latest/apidocs/io.helidon.config.encryption/io/helidon/config/encryption/EncryptionFilter.html) for details about encrypting passwords in configuration files. + client-secret: "client-secret-of-this-service" + identity-uri: "http://your-tenant.identity-server.com" + frontend-uri: "http://my-service:8080" + audience: "http://my-service" + cors: + allow-origins: ["http://foo.com", "http://there.com"] + allow-methods: ["PUT", "DELETE"] + outbound: + - name: "internal-services" + hosts: ["*.example.org"] + outbound-token: + header: "X-Internal-Auth" diff --git a/archetypes/helidon/src/main/archetype/common/files/pom.xml.mustache b/archetypes/helidon/src/main/archetype/common/files/pom.xml.mustache index 81a10de31a1..d1f7bc071c7 100644 --- a/archetypes/helidon/src/main/archetype/common/files/pom.xml.mustache +++ b/archetypes/helidon/src/main/archetype/common/files/pom.xml.mustache @@ -26,11 +26,14 @@ {{groupId}} {{artifactId}} {{#version}} - {{.}} + {{.}} {{/version}} {{#scope}} {{.}} {{/scope}} +{{#type}} + {{.}} +{{/type}} {{/dependencies}} diff --git a/archetypes/helidon/src/main/archetype/common/files/src/main/resources/keystore.p12 b/archetypes/helidon/src/main/archetype/common/files/src/main/resources/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..ff2c52d6694ae5c23a1b15d971935a9fa33ba1d4 GIT binary patch literal 2700 zcmY+Ec{~#iAIG;fW;V-2DtDUXoU@#B42?nvIYJ`jCWcr>iw=G{b4=zcDl+BDl_FP` zBPtBdJ;%ruvd8m!J@h=5COb~4J8;LHvO@ZHJ~iCD_hCm>hq1d;cpNOfRN-rLrtG~DA7fj*Z}5AF z_$ys_{9U*iBv!m)d3S5gs_N3wD?iWmrP{=Xi(eF#!LIzmaGm><`{XkR`~{CVKR)q| zlo(=?2*H2`g+ydMrLno^vN_}S^&UP|xC6_=vhkiSl#h!tB{=>`6Vlfj7Z#@fG7sy- z$u$SE_GK*3cz+}No8S5j=z<0O{Pv4-3rR`95mtHA#d+z~!0MkB(maOKclQe8_*F9m z(wUvN)egV?wZpg7W3AYbnZkN3)PA*+;Pm4`=#kN-fU1JY6%r32TT^B%;bET-;@oL4 z)QkW4EqbjK1C@WYw*T&&VRVW6mPxAi+Q@wqLIou!CuvcrEwzbiE&Bc}(^zX|ub^k1W&!{N+!#=N;Kiw$}CS-bpXXN&qw2)4>_P@G2>iv6D z36;EKaZZgBGMc{IyR*WpnH!-lw9GK1hE?}O)a0Cl_gc2^rO~L@(p`arZf6nH4o{E6 z-b!NJ%hJqdL*dt~lrpFDkpd)0fEs)OX+$NI|8A`kjg~L6J0>doEcsnk^eNs*6iQ`v zE9Pb9=8ZAn**BOtR&9aIXnRI$CPQM#-iYnGKOTHCMJ&O0(XxhmYlpb1Z96k~ArVEH ziP~XqlIx%AeO^sG1->(q;_L0ScUfAn*SIOdZ^87Z;85P&!*j{#K<^bdHROVtzwQdmsrqNoC}# za<$rb<`)P3wU_0dR{8YmVayHLj+j9b=T>UZZ$%|~Psgyk4kRc#bx!ZgW!!Z&SNM9Z zT;FbP;%7z|$Vz#E7rcs3e=@u=5Rf+WE`BQQHJo$Ba--DYJ`fW4VIV|PU#(a!$4O414=9`e@k@GxvlxT&?N5|VmStzwo^N{ z!4+O}#QKGE&M7S<-u~?|)v08vy+Od93HiTbC;9}K|LEhW)b&d%hh_xaBLP}JbFeLtJ0K7kaEA!Se@W`@w!!rY*PTOi z^h^`*I?syApRb=Sln^Mw`?vB@dc9!iz$K2%!09(h6-+Sz1GWxg_~l*VPy z6HMfzPgecmcG|U{YEap@X?nA|8Z#6o`X|w6X5hx`<0)-K7EH@$I%ML?^NBoUQ}NP3 z%d#XU%=il@WcZ!vWk3-dfv_6w6zT6CIS!$jy&W@dUsu`x(;|}m{jzz7+&pD?+UNBc z&U^1u5#G$*BS9O(L$Qn_yY)?SaXa+4+_doN&}qm|IWrl@uhzGlCB=6WxHuqr97gRj z8&$%uL%wPBT9P03al|JX^jeHdPECuUUh7eH5sG4X44F*F1!2Yv;bPApY%0$EAnguY z?T!~~hdme;5A7zPqr&mw8;9j|&)p%1o(2-cNtN#j;Nu*|iG>dA8B zLfF~wRaa;XFwI{&v;n5g^-S~#iL^Cc7lV~jKGp|Fe~{3^kc-H=B>Zry5*Jj1w6ZIL z*%T_$n@f#=LwzZZyuy~~#=YF&-~jRTB7xqo)_!F@ZDL|fhL`T!uV(GVSC95;ztj6! z>`B4Wg&70Vk=}l}xr3ru`Nb(|jh*u~{Yw^pV%Xj%$;ip1Y?+F!KXq%>75efgpB+PO zKMn!t(=62BLelCW6SSYX+LV*}cp#!ILHViysg>Zk?`&>S)1tWLowHp^ZL;sgjd=Hh zerVY*t_c$|#gv&(K620F2HKe~C~AGSq3>MfX*Z4c%AGkMD!DAwrIvMOH#{40#bR%4KD-KeglFJ!nwuxQOG_EmBjYC;^!!|aU=04_R`CyQPQg0@;F;om)sMnLo-ksGiQKopsaoi6=`G`sOs%^<+0x z)yU~1+YKxpF)Nzqz0!=_mDjty0)niQ3M|m&Hn2(4R6SEihAr{>$6Ig_4P4&nbfVkO z9@M>!-r}sqVQ4dTNJ|Ikv`pK%`76#eb-HY?;gJ!BX%s_NP?@KFxcsLD)qXC~QR~$5 z;d>#LBNES+Sp1Oi#pk7{uFQM-*PBbj#P76}+xgRsX)wQ;_{Y4q7bjgRsDbZ2N + + + + + + + files + + src/*/java/**/*.java.jsonp.mustache + + + + files + + src/*/java/**/*.json.mustache + + + + files + + src/*/java/**/*.multipart.mustache + + + + diff --git a/archetypes/helidon/src/main/archetype/common/media.xml b/archetypes/helidon/src/main/archetype/common/media.xml new file mode 100644 index 00000000000..1f7ad6328e1 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/media.xml @@ -0,0 +1,171 @@ + + + + + + + jackson + jsonp + + + + + + + + + + + + + + + ${media.json-lib} + true + + + + diff --git a/archetypes/helidon/src/main/archetype/common/observability.xml b/archetypes/helidon/src/main/archetype/common/observability.xml new file mode 100644 index 00000000000..26f7bc0d36a --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/observability.xml @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + io.helidon.metrics.MetricsSupport + + + + + + + + + + + + + + + + + + + + + + + + io.helidon.microprofile.health + helidon-microprofile-health + + + io.helidon.health + helidon-health + + + io.helidon.health + helidon-health-checks + + + + io.helidon.health.HealthSupport + io.helidon.health.checks.HealthChecks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + ${metrics.provider} + ${metrics.builtin} + true + ${health.builtin} + true + ${tracing.provider} + + + + diff --git a/archetypes/helidon/src/main/archetype/common/packaging.xml b/archetypes/helidon/src/main/archetype/common/packaging.xml new file mode 100644 index 00000000000..e49202493bb --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/packaging.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/common/presets.xml b/archetypes/helidon/src/main/archetype/common/presets.xml new file mode 100644 index 00000000000..c659040f635 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/presets.xml @@ -0,0 +1,41 @@ + + + + + + + json + + true + microprofile + true + true + true + false + + false + false + false + false + false + false + + diff --git a/archetypes/helidon/src/main/archetype/common/security.xml b/archetypes/helidon/src/main/archetype/common/security.xml new file mode 100644 index 00000000000..d2496a5e432 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/common/security.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + true + + + io.helidon.microprofile + helidon-microprofile-security + + + + + io.helidon.security + helidon-security + + + io.helidon.security.integration + helidon-security-integration-webserver + + + + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/common/project-sources.xml b/archetypes/helidon/src/main/archetype/common/sources.xml similarity index 91% rename from archetypes/helidon/src/main/archetype/common/project-sources.xml rename to archetypes/helidon/src/main/archetype/common/sources.xml index af67e76a4a2..b2e89ea4091 100644 --- a/archetypes/helidon/src/main/archetype/common/project-sources.xml +++ b/archetypes/helidon/src/main/archetype/common/sources.xml @@ -1,7 +1,7 @@ + + + + + + + + + files + + src/*/resources/keystore.p12 + + Minimal Helidon MP project suitable to start from scratch. @@ -32,18 +45,30 @@ - + jakarta.json.JsonObject - + + + + @@ -53,20 +78,13 @@ helidon-microprofile-core - io.helidon.microprofile.metrics - helidon-microprofile-metrics - - - io.helidon.microprofile.health - helidon-microprofile-health - - - io.helidon.microprofile.bundles - helidon-microprofile-core + org.glassfish.jersey.media + jersey-media-json-binding + runtime - io.helidon.media - helidon-media-jsonp + io.helidon.webclient + helidon-webclient diff --git a/archetypes/helidon/src/main/archetype/mp/custom/database.xml b/archetypes/helidon/src/main/archetype/mp/custom/database.xml new file mode 100644 index 00000000000..73f6c93878d --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/database.xml @@ -0,0 +1,435 @@ + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + hibernate.hbm2ddl.auto + create-drop + + + eclipselink.ddl-generation + drop-and-create-tables + + + + + + + + + + + ../database/files + + src/*/resources/*/init_script.sql + src/test/resources/*/microprofile-config.properties + + + + ../database/files + + src/main/java/** + src/*/resources/*/persistence.xml.mustache + + + + true + ${pu-name} + ${ds-name} + + jakarta.json.JsonArray + jakarta.ws.rs.core.MediaType + jakarta.ws.rs.client.Entity + + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + jakarta.ws.rs + jakarta.ws.rs-api + + + jakarta.persistence + jakarta.persistence-api + + + jakarta.transaction + jakarta.transaction-api + + + io.helidon.common + helidon-common + + + jakarta.xml.bind + jakarta.xml.bind-api + + + com.h2database + h2 + + + io.helidon.integrations.cdi + helidon-integrations-cdi-jta-weld + runtime + + + io.helidon.integrations.cdi + helidon-integrations-cdi-jpa + runtime + + + org.hibernate.validator + hibernate-validator + 7.0.2.Final + runtime + + + org.glassfish + jakarta.el + runtime + + + + + com.evolvedbinary.maven.jvnet + jaxb30-maven-plugin + 0.15.0 + + + Generate persistence.xml Java objects + + generate + + + io.helidon.archetypes.tests.jaxb + true + + + + jakarta.persistence + jakarta.persistence-api + jakarta/persistence/persistence_3_0.xsd + + + + false + + + + ]]> + + + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/mp/bare/files/README.md b/archetypes/helidon/src/main/archetype/mp/custom/files/README.md similarity index 100% rename from archetypes/helidon/src/main/archetype/mp/bare/files/README.md rename to archetypes/helidon/src/main/archetype/mp/custom/files/README.md diff --git a/archetypes/helidon/src/main/archetype/mp/bare/files/microprofile-config.properties b/archetypes/helidon/src/main/archetype/mp/custom/files/microprofile-config.properties similarity index 100% rename from archetypes/helidon/src/main/archetype/mp/bare/files/microprofile-config.properties rename to archetypes/helidon/src/main/archetype/mp/custom/files/microprofile-config.properties diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache new file mode 100644 index 00000000000..e976869cef7 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache @@ -0,0 +1,88 @@ +package {{package}}; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; +import org.glassfish.jersey.media.multipart.BodyPart; +import org.glassfish.jersey.media.multipart.BodyPartEntity; +import org.glassfish.jersey.media.multipart.MultiPart; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Map; + +/** + * File service. + */ +@Path("/multipart") +@ApplicationScoped +public class FileService { + + private static final JsonBuilderFactory JSON_FACTORY = Json.createBuilderFactory(Map.of()); + + private final FileStorage storage; + + @Inject + FileService(FileStorage storage) { + this.storage = storage; + } + + /** + * Upload a file to the storage. + * @param multiPart multipart entity + * @return Response + * @throws IOException if an IO error occurs + */ + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Response upload(MultiPart multiPart) throws IOException { + for (BodyPart part : multiPart.getBodyParts()) { + if ("file[]".equals(part.getContentDisposition().getParameters().get("name"))) { + Files.copy(part.getEntityAs(BodyPartEntity.class).getInputStream(), + storage.create(part.getContentDisposition().getFileName()), + StandardCopyOption.REPLACE_EXISTING); + } + } + return Response.ok().build(); + } + + /** + * Download a file from the storage. + * @param fname file name of the file to download + * @return Response + */ + @GET + @Path("{fname}") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response download(@PathParam("fname") String fname) { + return Response.ok() + .header("Content-Disposition", "attachment; filename=\"" + fname + "\"") + .entity((StreamingOutput) output -> Files.copy(storage.lookup(fname), output)) + .build(); + } + + /** + * List the files in the storage. + * @return JsonObject + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject list() { + JsonArrayBuilder arrayBuilder = JSON_FACTORY.createArrayBuilder(); + storage.listFiles().forEach(arrayBuilder::add); + return JSON_FACTORY.createObjectBuilder().add("files", arrayBuilder).build(); + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache new file mode 100644 index 00000000000..e832e079db0 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache @@ -0,0 +1,89 @@ +package {{package}}; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.NotFoundException; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +/** + * Simple bean to managed a directory based storage. + */ +@ApplicationScoped +public class FileStorage { + + private final Path storageDir; + + /** + * Create a new instance. + */ + public FileStorage() { + try { + storageDir = Files.createTempDirectory("fileupload"); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + /** + * Get the storage directory. + * @return directory + */ + public Path storageDir() { + return storageDir; + } + + /** + * Get the names of the files in the storage directory. + * @return Stream of file names + */ + public Stream listFiles() { + try { + return Files.walk(storageDir) + .filter(Files::isRegularFile) + .map(storageDir::relativize) + .map(Path::toString); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + /** + * Create a new file in the storage. + * @param fname file name + * @return file + * @throws BadRequestException if the resolved file is not contained in the storage directory + */ + public Path create(String fname) { + Path file = storageDir.resolve(fname); + if (!file.getParent().equals(storageDir)) { + throw new BadRequestException("Invalid file name"); + } + return file; + } + + /** + * Lookup an existing file in the storage. + * @param fname file name + * @return file + * @throws NotFoundException If the resolved file does not exist + * @throws BadRequestException if the resolved file is not contained in the storage directory + */ + public Path lookup(String fname) { + Path file = storageDir.resolve(fname); + if (!file.getParent().equals(storageDir)) { + throw new BadRequestException("Invalid file name"); + } + if (!Files.exists(file)) { + throw new NotFoundException(); + } + if (!Files.isRegularFile(file)) { + throw new BadRequestException("Not a file"); + } + return file; + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FtResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FtResource.java.mustache new file mode 100644 index 00000000000..97a16e4d633 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/FtResource.java.mustache @@ -0,0 +1,57 @@ +package {{package}}; + +import io.helidon.common.reactive.Single; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import org.eclipse.microprofile.faulttolerance.Fallback; +import org.eclipse.microprofile.faulttolerance.Retry; + +@Path("/ft") +public class FtResource { + + private static int retry; + + @Fallback(fallbackMethod = "fallbackMethod") + @Path("/fallback/{success}") + @GET + public Single fallbackHandler(@PathParam("success") String success) { + if (!Boolean.parseBoolean(success)) { + deadEnd(); + } + return reactiveData(); + } + + @Retry(maxRetries = 2) + @Path("/retry") + @GET + public Single retryHandler() { + if (++retry < 2) { + deadEnd(); + } + String response = String.format("failures: %s", retry); + retry = 0; + return Single.just(response); + } + + private void deadEnd() { + throw new RuntimeException("failure"); + } + + private Single fallbackMethod(String success) { + return Single.just("Fallback endpoint reached"); + } + + private Single reactiveData() { + return Single.create(this::blockingData); + } + + private String blockingData() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + return "blocked for 100 millis"; + } + +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/Message.java.json.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/Message.java.json.mustache new file mode 100644 index 00000000000..92e2e664d60 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/Message.java.json.mustache @@ -0,0 +1,31 @@ +package {{package}}; + +public class Message { + + private String message; + + private String greeting; + + public Message() { + } + + public Message(String message) { + this.message = message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + + public void setGreeting(String greeting) { + this.greeting = greeting; + } + + public String getGreeting() { + return this.greeting; + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/MultiPartFeatureProvider.java.multipart.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/MultiPartFeatureProvider.java.multipart.mustache new file mode 100644 index 00000000000..7eb10dafa5c --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/MultiPartFeatureProvider.java.multipart.mustache @@ -0,0 +1,19 @@ +package {{package}}; + +import jakarta.ws.rs.core.Feature; +import jakarta.ws.rs.core.FeatureContext; +import jakarta.ws.rs.ext.Provider; +import org.glassfish.jersey.media.multipart.MultiPartFeature; + +/** + * {@link MultiPartFeature} is not auto-discovered. This {@link Feature} is discovered with {@link @Provider} + * and registers {@link MultiPartFeature} manually. + */ +@Provider +public class MultiPartFeatureProvider implements Feature { + + @Override + public boolean configure(FeatureContext context) { + return new MultiPartFeature().configure(context); + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.json.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.json.mustache new file mode 100644 index 00000000000..52ba16ef2bd --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.json.mustache @@ -0,0 +1,57 @@ + +package {{package}}; + +import java.util.Collections; + +{{#SimpleGreetService-imports}} +import {{.}}; +{{/SimpleGreetService-imports}} + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * A simple JAX-RS resource to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/simple-greet + * + * The message is returned as a JSON object. + */ +@Path("/simple-greet") +public class SimpleGreetResource { +{{#SimpleGreetResource-static-fields}} +{{.}} +{{/SimpleGreetResource-static-fields}} + private final String message; + + @Inject + public SimpleGreetResource(@ConfigProperty(name = "app.greeting") String message) { + this.message = message; + } + + /** + * Return a worldly greeting message. + * + * @return {@link Message} + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getDefaultMessage() { + String msg = String.format("%s %s!", message, "World"); + Message message = new Message(); + message.setMessage(msg); + return message; + } + +{{#SimpleGreetService-methods}} +{{.}} +{{/SimpleGreetService-methods}} + +} diff --git a/archetypes/helidon/src/main/archetype/mp/bare/files/src/main/java/__pkg__/GreetResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.jsonp.mustache similarity index 72% rename from archetypes/helidon/src/main/archetype/mp/bare/files/src/main/java/__pkg__/GreetResource.java.mustache rename to archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.jsonp.mustache index 463856b8b7a..58d6665b2b3 100644 --- a/archetypes/helidon/src/main/archetype/mp/bare/files/src/main/java/__pkg__/GreetResource.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.jsonp.mustache @@ -3,6 +3,10 @@ package {{package}}; import java.util.Collections; +{{#SimpleGreetService-imports}} +import {{.}}; +{{/SimpleGreetService-imports}} + import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.json.Json; @@ -19,18 +23,21 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; * A simple JAX-RS resource to greet you. Examples: * * Get default greeting message: - * curl -X GET http://localhost:8080/greet + * curl -X GET http://localhost:8080/simple-greet * * The message is returned as a JSON object. */ -@Path("/greet") -public class GreetResource { +@Path("/simple-greet") +public class SimpleGreetResource { +{{#SimpleGreetResource-static-fields}} +{{.}} +{{/SimpleGreetResource-static-fields}} private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); private final String message; @Inject - public GreetResource(@ConfigProperty(name = "app.greeting") String message) { + public SimpleGreetResource(@ConfigProperty(name = "app.greeting") String message) { this.message = message; } @@ -47,4 +54,9 @@ public class GreetResource { .add("message", msg) .build(); } + +{{#SimpleGreetService-methods}} +{{.}} +{{/SimpleGreetService-methods}} + } diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache new file mode 100644 index 00000000000..7b967c0b327 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache @@ -0,0 +1,101 @@ + +package {{package}}; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import io.helidon.microprofile.server.JaxRsCdiExtension; +import io.helidon.microprofile.server.ServerCdiExtension; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; + +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider; +import org.glassfish.jersey.media.multipart.MultiPart; +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Tests {@link FileService}. + */ +@HelidonTest +@DisableDiscovery +@AddExtension(ServerCdiExtension.class) +@AddExtension(JaxRsCdiExtension.class) +@AddExtension(CdiComponentProvider.class) +@AddBean(FileService.class) +@AddBean(FileStorage.class) +@AddBean(MultiPartFeatureProvider.class) +@TestMethodOrder(OrderAnnotation.class) +public class FileServiceTest { + + @Test + @Order(1) + public void testUpload(WebTarget target) throws IOException { + Path tempDirectory = Files.createTempDirectory(null); + File file = Files.write(tempDirectory.resolve("foo.txt"), "bar\n".getBytes(StandardCharsets.UTF_8)).toFile(); + MultiPart multipart = new MultiPart() + .bodyPart(new FileDataBodyPart("file[]", file, MediaType.APPLICATION_OCTET_STREAM_TYPE)); + try (Response response = target + .property(ClientProperties.FOLLOW_REDIRECTS, Boolean.FALSE) + .register(MultiPartFeature.class) + .path("/multipart") + .request() + .post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA_TYPE))) { + assertThat(response.getStatus(), is(200)); + } + } + + @Test + @Order(2) + public void testList(WebTarget target) { + try (Response response = target + .path("/multipart") + .request(MediaType.APPLICATION_JSON_TYPE) + .get()) { + assertThat(response.getStatus(), is(200)); + JsonObject json = response.readEntity(JsonObject.class); + assertThat(json, is(notNullValue())); + List files = json.getJsonArray("files").getValuesAs(v -> ((JsonString) v).getString()); + assertThat(files, hasItem("foo.txt")); + } + } + + @Test + @Order(3) + public void testDownload(WebTarget target) throws IOException { + try (Response response = target + .register(MultiPartFeature.class) + .path("/multipart/foo.txt") + .request(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .get()) { + assertThat(response.getStatus(), is(200)); + assertThat(response.getHeaderString("Content-Disposition"), containsString("filename=\"foo.txt\"")); + InputStream inputStream = response.readEntity(InputStream.class); + assertThat(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8), is("bar\n")); + } + } +} \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FtResourceTest.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FtResourceTest.java.mustache new file mode 100644 index 00000000000..fbbb594e584 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/FtResourceTest.java.mustache @@ -0,0 +1,60 @@ +package {{package}}; + +import io.helidon.microprofile.server.Server; +import io.helidon.webclient.WebClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class FtResourceTest { + + private static Server server; + private static WebClient client; + + @BeforeAll + static void initClass() { + server = Server.create().start(); + + client = WebClient.builder() + .baseUri("http://localhost:" + server.port() + "/ft") + .build(); + } + + @AfterAll + static void destroyClass() { + server.stop(); + } + + @Test + void testFallback() { + String response = client.get() + .path("/fallback/true") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("blocked for 100 millis")); + + response = client.get() + .path("/fallback/false") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("Fallback endpoint reached")); + } + + @Test + void testRetry() { + String response = client.get() + .path("/retry") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("failures: 2")); + } + +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/TestCORS.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/TestCORS.java.mustache new file mode 100644 index 00000000000..62c90704e32 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/test/java/__pkg__/TestCORS.java.mustache @@ -0,0 +1,140 @@ +package {{package}}; + +import io.helidon.common.http.Headers; +import io.helidon.config.Config; +import io.helidon.microprofile.server.Server; +import io.helidon.webclient.WebClient; +import io.helidon.webclient.WebClientRequestBuilder; +import io.helidon.webclient.WebClientResponse; +import io.helidon.webserver.cors.CrossOriginConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestCORS { + + private static WebClient client; + private static Server server; + + @BeforeAll + static void init() { + Config serverConfig = Config.create().get("server"); + Server.Builder serverBuilder = Server.builder(); + serverConfig.ifExists(serverBuilder::config); + server = serverBuilder + .port(-1) // override the port for testing + .build() + .start(); + client = WebClient.builder() + .baseUri("http://localhost:" + server.port()) + .build(); + } + + @AfterAll + static void cleanup() { + if (server != null) { + server.stop(); + } + } + + @Test + void testAnonymousGreetWithCors() { + WebClientRequestBuilder builder = client.get(); + Headers headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + + WebClientResponse r = getResponse(builder); + assertThat("HTTP response", r.status().code(), is(200)); + String payload = fromPayload(r); + assertThat("HTTP response payload", payload, is("{\"message\":\"Hello World!\"}")); + headers = r.headers(); + Optional allowOrigin = headers.value(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("Expected CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN + " is present", + allowOrigin.isPresent(), is(true)); + assertThat("CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin.get(), is("http://foo.com")); + } + + @Test + void testCustomGreetingWithCors() { + + WebClientRequestBuilder builder = client.method("OPTIONS"); + Headers headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + headers.add("Access-Control-Request-Method", "PUT"); + + WebClientResponse r = builder.path("/simple-greet") + .submit() + .await(); + + assertThat("pre-flight status", r.status().code(), is(200)); + Headers preflightResponseHeaders = r.headers(); + List allowMethods = preflightResponseHeaders.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS); + assertThat("pre-flight response check for " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS, + allowMethods, is(not(empty()))); + assertThat("Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS, allowMethods, contains("PUT")); + List allowOrigins = preflightResponseHeaders.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("pre-flight response check for " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, + allowOrigins, is(not(empty()))); + assertThat( "Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigins, contains("http://foo.com")); + + builder = client.put(); + headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + headers.addAll(preflightResponseHeaders); + + r = putResponse("Cheers", builder); + assertThat("HTTP response3", r.status().code(), is(200)); + headers = r.headers(); + allowOrigins = headers.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("Expected CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, + allowOrigins, is(not(empty()))); + assertThat( "Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigins, contains("http://foo.com")); + assertThat(fromPayload(r), containsString("Cheers World!")); + } + + @Test + void testGreetingChangeWithCorsAndOtherOrigin() { + WebClientRequestBuilder builder = client.put(); + Headers headers = builder.headers(); + headers.add("Origin", "http://other.com"); + headers.add("Host", "here.com"); + + WebClientResponse r = putResponse("Ahoy", builder); + boolean isOverriding = Config.create().get("cors").exists(); + assertThat("HTTP response3", r.status().code(), is(isOverriding ? 204 : 403)); + } + + + private static WebClientResponse getResponse(WebClientRequestBuilder builder) { + return builder + .path("/simple-greet") + .submit() + .await(); + } + + private static String fromPayload(WebClientResponse response) { + return response + .content() + .as(String.class) + .await(); + } + + private static WebClientResponse putResponse(String message, WebClientRequestBuilder builder) { + return builder + .path("/simple-greet") + .submit(message) + .await(); + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml b/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml index 4a43be64adf..c0beb048284 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml @@ -1,7 +1,7 @@ - - org.hibernate.validator - hibernate-validator - 7.0.2.Final - runtime - - - org.glassfish - jakarta.el - runtime - - - - - - com.evolvedbinary.maven.jvnet - jaxb30-maven-plugin - 0.15.0 - - - Generate persistence.xml Java objects - - generate - - - io.helidon.archetypes.tests.jaxb - true - - - - jakarta.persistence - jakarta.persistence-api - jakarta/persistence/persistence_3_0.xsd - - - - false - - - - - - org.hibernate.orm.tooling - hibernate-enhance-maven-plugin - - - - true - true - true - - - enhance - - - - ]]> - - - - - - jakarta.ws.rs.core.MediaType - jakarta.json.JsonArray - jakarta.json.JsonObject - - - io.helidon.media.jsonp.JsonpSupport - - - - diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/microprofile-config.properties b/archetypes/helidon/src/main/archetype/mp/database/files/microprofile-config.properties deleted file mode 100644 index 81118dc5c95..00000000000 --- a/archetypes/helidon/src/main/archetype/mp/database/files/microprofile-config.properties +++ /dev/null @@ -1,6 +0,0 @@ -# Datasource properties -javax.sql.DataSource.test.dataSourceClassName=org.h2.jdbcx.JdbcDataSource -javax.sql.DataSource.test.dataSource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 -#javax.sql.DataSource.test.dataSource.url=jdbc:h2:tcp://localhost:1521/test -javax.sql.DataSource.test.dataSource.user=sa -javax.sql.DataSource.test.dataSource.password= diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache index 29ff0a17886..3c7085ccad9 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache @@ -1,7 +1,6 @@ package {{package}}; -import jakarta.json.bind.annotation.JsonbTransient; import jakarta.persistence.Access; import jakarta.persistence.AccessType; import jakarta.persistence.Basic; @@ -35,7 +34,6 @@ public class Pokemon { private String name; - @JsonbTransient private PokemonType pokemonType; private int type; diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/META-INF/persistence.xml.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/META-INF/persistence.xml.mustache index 3f0d4a3901e..437cc6fec74 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/META-INF/persistence.xml.mustache +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/META-INF/persistence.xml.mustache @@ -21,14 +21,17 @@ xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd" version="3.0"> - - test + + {{ds-name}} {{package}}.Pokemon {{package}}.PokemonType - - - + {{#common-persistence-properties}} + + {{/common-persistence-properties}} + {{#main-persistence-properties}} + + {{/main-persistence-properties}} diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/hibernate.properties b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/hibernate.properties.mustache similarity index 100% rename from archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/hibernate.properties rename to archetypes/helidon/src/main/archetype/mp/database/files/src/main/resources/hibernate.properties.mustache diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/test/java/__pkg__/MainTest.java.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/test/java/__pkg__/MainTest.java.mustache deleted file mode 100644 index c39b568dc81..00000000000 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/test/java/__pkg__/MainTest.java.mustache +++ /dev/null @@ -1,111 +0,0 @@ - -package {{package}}; - -import jakarta.enterprise.inject.se.SeContainer; -import jakarta.enterprise.inject.spi.CDI; -import jakarta.json.JsonArray; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -import io.helidon.microprofile.server.Server; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -class MainTest { - - private static Server server; - private static String serverUrl; - private static Client client; - - @BeforeAll - public static void startTheServer() { - client = ClientBuilder.newClient(); - server = Server.create().start(); - serverUrl = "http://localhost:" + server.port(); - } - - @AfterAll - static void destroyClass() { - CDI current = CDI.current(); - ((SeContainer) current).close(); - } - - @Test - void testPokemonTypes() { - JsonArray types = client.target(serverUrl) - .path("type") - .request() - .get(JsonArray.class); - assertThat(types.size(), is(18)); - } - - @Test - void testPokemon() { - assertThat(getPokemonCount(), is(6)); - - Pokemon pokemon = client.target(serverUrl) - .path("pokemon/1") - .request() - .get(Pokemon.class); - assertThat(pokemon.getName(), is("Bulbasaur")); - - pokemon = client.target(serverUrl) - .path("pokemon/name/Charmander") - .request() - .get(Pokemon.class); - assertThat(pokemon.getType(), is(10)); - - Response response = client.target(serverUrl) - .path("pokemon/1") - .request() - .get(); - assertThat(response.getStatus(), is(200)); - - Pokemon test = new Pokemon(); - test.setType(1); - test.setId(100); - test.setName("Test"); - response = client.target(serverUrl) - .path("pokemon") - .request() - .post(Entity.entity(test, MediaType.APPLICATION_JSON)); - assertThat(response.getStatus(), is(204)); - assertThat(getPokemonCount(), is(7)); - - response = client.target(serverUrl) - .path("pokemon/100") - .request() - .delete(); - assertThat(response.getStatus(), is(204)); - assertThat(getPokemonCount(), is(6)); - } - - private int getPokemonCount() { - JsonArray pokemons = client.target(serverUrl) - .path("pokemon") - .request() - .get(JsonArray.class); - return pokemons.size(); - } - - @Test - void testHealthMetrics() { - Response response = client.target(serverUrl) - .path("health") - .request() - .get(); - assertThat(response.getStatus(), is(200)); - response = client.target(serverUrl) - .path("metrics") - .request() - .get(); - assertThat(response.getStatus(), is(200)); - } -} diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/microprofile-config.properties b/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..6ce691b5e6b --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,16 @@ +# Microprofile server properties +server.port=8080 +server.host=0.0.0.0 + +# Change the following to true to enable the optional MicroProfile Metrics REST.request metrics +metrics.rest-request.enabled=false + +# Datasource properties +javax.sql.DataSource.test.dataSourceClassName=org.h2.jdbcx.JdbcDataSource +javax.sql.DataSource.test.dataSource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 +javax.sql.DataSource.test.dataSource.user=sa +javax.sql.DataSource.test.dataSource.password= + +# Application properties. This is the default greeting +app.greeting=Hello + diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/persistence.xml.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/persistence.xml.mustache new file mode 100644 index 00000000000..53f8e13cb12 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/persistence.xml.mustache @@ -0,0 +1,39 @@ + + + + + + test + {{package}}.Pokemon + {{package}}.PokemonType + + + + {{#common-persistence-properties}} + + {{/common-persistence-properties}} + {{#test-persistence-properties}} + + {{/test-persistence-properties}} + + + diff --git a/archetypes/helidon/src/main/archetype/mp/mp.xml b/archetypes/helidon/src/main/archetype/mp/mp.xml index ede21e7c6fb..6e6fc59fb07 100644 --- a/archetypes/helidon/src/main/archetype/mp/mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/mp.xml @@ -22,12 +22,11 @@ - - + + diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.json.mustache b/archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.json.mustache new file mode 100644 index 00000000000..98433f1f4ef --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.json.mustache @@ -0,0 +1,115 @@ + +package {{package}}; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; + +/** + * A simple JAX-RS resource to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/greet + * + * Get greeting message for Joe: + * curl -X GET http://localhost:8080/greet/Joe + * + * Change greeting + * curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting + * + * The message is returned as a JSON object. + */ +@Path("/greet") +@RequestScoped +public class GreetResource { + + /** + * The greeting message provider. + */ + private final GreetingProvider greetingProvider; + + /** + * Using constructor injection to get a configuration property. + * By default this gets the value from META-INF/microprofile-config + * + * @param greetingConfig the configured greeting message + */ + @Inject + public GreetResource(GreetingProvider greetingConfig) { + this.greetingProvider = greetingConfig; + } + + /** + * Return a worldly greeting message. + * + * @return {@link Message} + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getDefaultMessage() { + return createResponse("World"); + } + + /** + * Return a greeting message using the name that was provided. + * + * @param name the name to greet + * @return {@link Message} + */ + @Path("/{name}") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getMessage(@PathParam("name") String name) { + return createResponse(name); + } + + /** + * Set the greeting to use in future messages. + * + * @param jsonNode JSON containing the new greeting + * @return {@link Response} + */ + @Path("/greeting") + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @RequestBody(name = "greeting", + required = true, + content = @Content(mediaType = "application/json", + schema = @Schema(type = SchemaType.OBJECT, requiredProperties = { "greeting" }))) + @APIResponses({ + @APIResponse(name = "normal", responseCode = "204", description = "Greeting updated"), + @APIResponse(name = "missing 'greeting'", responseCode = "400", + description = "JSON did not contain setting for 'greeting'")}) + public Response updateGreeting(Message message) { + + if (message.getGreeting() == null || message.getGreeting().isEmpty()) { + Message error = new Message(); + error.setMessage("No greeting provided"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).build(); + } + + greetingProvider.setMessage(message.getGreeting()); + return Response.status(Response.Status.NO_CONTENT).build(); + } + + private Message createResponse(String who) { + String msg = String.format("%s %s!", greetingProvider.getMessage(), who); + + return new Message(msg); + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.jsonp.mustache similarity index 100% rename from archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.mustache rename to archetypes/helidon/src/main/archetype/mp/quickstart/files/src/main/java/__pkg__/GreetResource.java.jsonp.mustache diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml index 2489d271ddf..0946260d8b5 100644 --- a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml @@ -1,7 +1,7 @@ - - - - - - - /xyz-files - - io/helidon/archetype/template/se/GreetService.java - - - - Minimal Helidon SE project suitable to start from scratch. - - - - - - io.helidon.media - helidon-media-jsonp - - - - io.helidon.media.jsonp.JsonpSupport - - - - - - - - - - - System.out.println("WEB server is up! http://localhost:" + ws.port() + "/greet"); - - jakarta.json.JsonObject - - - io.helidon.media.jsonp.JsonpSupport - - - org.junit.jupiter.api.Order - - - - - - - - - - - - - diff --git a/archetypes/helidon/src/main/archetype/se/common/common-se.xml b/archetypes/helidon/src/main/archetype/se/common/common-se.xml index 97773c24203..fe1d510486c 100644 --- a/archetypes/helidon/src/main/archetype/se/common/common-se.xml +++ b/archetypes/helidon/src/main/archetype/se/common/common-se.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + Minimal Helidon SE project suitable to start from scratch. + + + + + + + + + + System.out.println("WEB server is up! http://localhost:" + ws.port() + "/simple-greet"); + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/custom/custom-tests.xml b/archetypes/helidon/src/main/archetype/se/custom/custom-tests.xml new file mode 100644 index 00000000000..399959bb1c1 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/custom-tests.xml @@ -0,0 +1,54 @@ + + + + + + + + org.junit.jupiter.api.Order + + + jakarta.json.JsonObject + + + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/custom/database.xml b/archetypes/helidon/src/main/archetype/se/custom/database.xml new file mode 100644 index 00000000000..c90e6743612 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/database.xml @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + ../database/files + + src/*/java/**/*.java.mustache + src/*/resources/**/* + + + + + + io.helidon.dbclient + helidon-dbclient + + + io.helidon.dbclient + helidon-dbclient-jsonp + + + io.helidon.media + helidon-media-jsonb + + + io.helidon.media + helidon-media-jsonp + + + io.helidon.dbclient + helidon-dbclient-jdbc + + + io.helidon.dbclient + helidon-dbclient-health + + + io.helidon.dbclient + helidon-dbclient-metrics-jdbc + + + io.helidon.dbclient + helidon-dbclient-metrics + + + io.helidon.dbclient + helidon-dbclient-tracing + + + org.slf4j + slf4j-jdk14 + + + + io.helidon.dbclient.DbClient + io.helidon.media.jsonp.JsonpSupport + io.helidon.media.jsonb.JsonbSupport + io.helidon.dbclient.health.DbClientHealthCheck + + + + + + + + + + + + + + + System.out.println("Database here : http://localhost:" + ws.port() + "/pokemon"); + + handle.namedDml("create-table")) + .thenAccept(System.out::println) + .exceptionally(throwable -> { + LOGGER.log(Level.WARNING, "Failed to create table, maybe it already exists?", throwable); + return null; + });]]> + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/bare/files/README.md b/archetypes/helidon/src/main/archetype/se/custom/files/README.md similarity index 100% rename from archetypes/helidon/src/main/archetype/se/bare/files/README.md rename to archetypes/helidon/src/main/archetype/se/custom/files/README.md diff --git a/archetypes/helidon/src/main/archetype/se/database/files/application.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/application.h2.yaml similarity index 73% rename from archetypes/helidon/src/main/archetype/se/database/files/application.yaml rename to archetypes/helidon/src/main/archetype/se/custom/files/application.h2.yaml index f5d9f00e5b2..447751293f2 100644 --- a/archetypes/helidon/src/main/archetype/se/database/files/application.yaml +++ b/archetypes/helidon/src/main/archetype/se/custom/files/application.h2.yaml @@ -26,15 +26,16 @@ db: create-types: "CREATE TABLE POKEMONTYPE (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL)" create-pokemons: "CREATE TABLE POKEMON (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL, id_type INTEGER NOT NULL REFERENCES POKEMONTYPE(id))" # Select all types - select-all-types: "SELECT id, name FROM POKEMONTYPE" + select-all-types: "SELECT * FROM POKEMONTYPE" # Select all pokemons - select-all-pokemons: "SELECT id, name, id_type FROM POKEMON" + select-all-pokemons: "SELECT * FROM POKEMON" # Select pokemon by id - select-pokemon-by-id: "SELECT id, name, id_type FROM POKEMON WHERE id = :id" + select-pokemon-by-id: "SELECT * FROM POKEMON WHERE id = :id" # Select pokemon by name - select-pokemon-by-name: "SELECT id, name, id_type FROM POKEMON WHERE name = ?" + select-pokemon-by-name: "SELECT * FROM POKEMON WHERE name = ?" # Insert records into database - insert-type: "INSERT INTO POKEMONTYPE(id, name) VALUES(?, ?)" - insert-pokemon: "INSERT INTO POKEMON(id, name, id_type) VALUES(?, ?, ?)" + insert-type: "INSERT INTO POKEMONTYPE VALUES(?, ?)" + insert-pokemon: "INSERT INTO POKEMON VALUES(?, ?, ?)" # Delete pokemon by id delete-pokemon-by-id: "DELETE FROM POKEMON WHERE id = :id" + diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/application.mongo.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/application.mongo.yaml new file mode 100644 index 00000000000..0789ceb659d --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/application.mongo.yaml @@ -0,0 +1,58 @@ +db: + source: "mongoDb" + connection: + # docker run --rm --name mongo -p 27017:27017 mongo + url: "mongodb://127.0.0.1:27017/pokemon" + health-check: + type: "query" + statementName: "health-check" + statements: + create-types: "CREATE TABLE POKEMONTYPE (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL)" + create-pokemons: "CREATE TABLE POKEMON (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL, id_type INTEGER NOT NULL REFERENCES POKEMONTYPE(id))" + # Select all types + select-all-types: '{ + "collection": "POKEMONTYPE", + "query": {} + }' + # Select all pokemons + select-all-pokemons: '{ + "collection": "POKEMON", + "query": {} + }' + # Select pokemon by id + select-pokemon-by-id: '{ + "collection": "POKEMON", + "query": { + "id": $id + } + }' + # Select pokemon by name + select-pokemon-by-name: '{ + "collection": "POKEMON", + "query": { + "name": ? + } + }' + # Insert records into database + insert-type: '{ + "collection": "POKEMONTYPE", + "value": { + "id": ?, + "name": ? + } + }' + insert-pokemon: '{ + "collection": "POKEMON", + "value": { + "id": ?, + "name": ? + "id_type": ? + } + }' + # Delete pokemon by id + delete-pokemon-by-id: '{ + "collection": "POKEMON", + "query": { + "id": $id + } + }' diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/application.mysql.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/application.mysql.yaml new file mode 100644 index 00000000000..3a27937c8b5 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/application.mysql.yaml @@ -0,0 +1,40 @@ +db: + source: jdbc + connection: + # docker run --rm --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=pokemon -e MYSQL_USER=user -e MYSQL_PASSWORD=password mysql:5.7 + url: jdbc:mysql://127.0.0.1:3306/pokemon?useSSL=false + username: user + password: password + poolName: mysql + initializationFailTimeout: -1 + connectionTimeout: 2000 + helidon: + pool-metrics: + enabled: true + # name prefix defaults to "db.pool." - if you have more than one client within a JVM, you may want to distinguish between them + name-prefix: "hikari." + services: + metrics: + - type: METER + health-check: + type: "dml" + statementName: "ping" + statements: + # Ping statement + ping: "SET @HELIDON_PING=0" + # Create database schema + create-types: "CREATE TABLE POKEMONTYPE (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL)" + create-pokemons: "CREATE TABLE POKEMON (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL, id_type INTEGER NOT NULL REFERENCES POKEMONTYPE(id))" + # Select all types + select-all-types: "SELECT * FROM POKEMONTYPE" + # Select all pokemons + select-all-pokemons: "SELECT * FROM POKEMON" + # Select pokemon by id + select-pokemon-by-id: "SELECT * FROM POKEMON WHERE id = :id" + # Select pokemon by name + select-pokemon-by-name: "SELECT * FROM POKEMON WHERE name = ?" + # Insert records into database + insert-type: "INSERT INTO POKEMONTYPE VALUES(?, ?)" + insert-pokemon: "INSERT INTO POKEMON VALUES(?, ?, ?)" + # Delete pokemon by id + delete-pokemon-by-id: "DELETE FROM POKEMON WHERE id = :id" \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/application.oracledb.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/application.oracledb.yaml new file mode 100644 index 00000000000..50062914726 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/application.oracledb.yaml @@ -0,0 +1,40 @@ +db: + source: jdbc + connection: + # docker run --rm --name xe -p 1521:1521 -p 8888:8080 -e ORACLE_PWD=oracle wnameless/oracle-xe-11g-r2 + url: jdbc:oracle:thin:@localhost:1521/XE + username: system + password: oracle + poolName: oracle + initializationFailTimeout: -1 + connectionTimeout: 2000 + helidon: + pool-metrics: + enabled: true + # name prefix defaults to "db.pool." - if you have more than one client within a JVM, you may want to distinguish between them + name-prefix: "hikari." + services: + metrics: + - type: METER + health-check: + type: "dml" + statementName: "ping" + statements: + # Ping statement + ping: "SET @HELIDON_PING=0" + # Create database schema + create-types: "CREATE TABLE POKEMONTYPE (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL)" + create-pokemons: "CREATE TABLE POKEMON (id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(64) NOT NULL, id_type INTEGER NOT NULL REFERENCES POKEMONTYPE(id))" + # Select all types + select-all-types: "SELECT * FROM POKEMONTYPE" + # Select all pokemons + select-all-pokemons: "SELECT * FROM POKEMON" + # Select pokemon by id + select-pokemon-by-id: "SELECT * FROM POKEMON WHERE id = :id" + # Select pokemon by name + select-pokemon-by-name: "SELECT * FROM POKEMON WHERE name = ?" + # Insert records into database + insert-type: "INSERT INTO POKEMONTYPE VALUES(?, ?)" + insert-pokemon: "INSERT INTO POKEMON VALUES(?, ?, ?)" + # Delete pokemon by id + delete-pokemon-by-id: "DELETE FROM POKEMON WHERE id = :id" diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/CorsService.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/CorsService.java.mustache new file mode 100644 index 00000000000..70f09d48234 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/CorsService.java.mustache @@ -0,0 +1,25 @@ +package {{package}}; + +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +public class CorsService implements Service { + + @Override + public void update(Routing.Rules rules) { + rules.put("/{greeting}", this::getCustomMessage) + .get("/", this::getDefaultMessage); + } + + private void getCustomMessage(ServerRequest request, ServerResponse response) { + String msg = String.format("%s %s!", request.path().param("greeting"), "World"); + response.send(msg); + } + + private void getDefaultMessage(ServerRequest request, ServerResponse response) { + response.send("Hello World!"); + } + +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache new file mode 100644 index 00000000000..42d796c9fc4 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileService.java.multipart.mustache @@ -0,0 +1,86 @@ + +package {{package}}; + +import io.helidon.common.configurable.ThreadPoolSupplier; +import io.helidon.common.http.DataChunk; +import io.helidon.common.http.Http; +import io.helidon.common.http.MediaType; +import io.helidon.common.reactive.IoMulti; +import io.helidon.media.multipart.ContentDisposition; +import io.helidon.media.multipart.ReadableBodyPart; +import io.helidon.webserver.ResponseHeaders; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; +import jakarta.json.Json; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonBuilderFactory; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +/** + * File service. + */ +public final class FileService implements Service { + + private static final JsonBuilderFactory JSON_FACTORY = Json.createBuilderFactory(Map.of()); + private final FileStorage storage; + private final ExecutorService executor = ThreadPoolSupplier.create("multipart-thread-pool").get(); + + + /** + * Create a new file upload service instance. + */ + FileService() { + storage = new FileStorage(); + } + + @Override + public void update(Routing.Rules rules) { + rules.get("/", this::list) + .get("/{fname}", this::download) + .post("/", this::upload); + } + + private void list(ServerRequest req, ServerResponse res) { + JsonArrayBuilder arrayBuilder = JSON_FACTORY.createArrayBuilder(); + storage.listFiles().forEach(arrayBuilder::add); + res.send(JSON_FACTORY.createObjectBuilder().add("files", arrayBuilder).build()); + } + + private void download(ServerRequest req, ServerResponse res) { + Path filePath = storage.lookup(req.path().param("fname")); + ResponseHeaders headers = res.headers(); + headers.contentType(MediaType.APPLICATION_OCTET_STREAM); + headers.put(Http.Header.CONTENT_DISPOSITION, ContentDisposition.builder() + .filename(filePath.getFileName().toString()) + .build() + .toString()); + res.send(filePath); + } + + private void upload(ServerRequest req, ServerResponse res) { + req.content().asStream(ReadableBodyPart.class) + .forEach(part -> { + if ("file[]".equals(part.name())) { + part.content().map(DataChunk::data) + .flatMapIterable(Arrays::asList) + .to(IoMulti.writeToFile(storage.create(part.filename())) + .executor(executor) + .build()); + } else { + // when streaming unconsumed parts needs to be drained + part.drain(); + } + }) + .onError(res::send) + .onComplete(() -> { + res.status(Http.Status.OK_200); + res.send(); + }).ignoreElement(); + } +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache new file mode 100644 index 00000000000..b714b87707a --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FileStorage.java.multipart.mustache @@ -0,0 +1,97 @@ + +package {{package}}; + +import io.helidon.webserver.BadRequestException; +import io.helidon.webserver.NotFoundException; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +/** + * Simple bean to managed a directory based storage. + */ +public class FileStorage { + + private final Path storageDir; + + /** + * Create a new instance. + */ + public FileStorage() { + try { + storageDir = Files.createTempDirectory("fileupload"); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + /** + * Get the storage directory. + * + * @return directory + */ + public Path storageDir() { + return storageDir; + } + + /** + * Get the names of the files in the storage directory. + * + * @return Stream of file names + */ + public Stream listFiles() { + try { + return Files.walk(storageDir) + .filter(Files::isRegularFile) + .map(storageDir::relativize) + .map(Path::toString); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + /** + * Create a new file in the storage. + * + * @param fname file name + * @return file + * @throws BadRequestException if the resolved file is not contained in the storage directory + */ + public Path create(String fname) { + Path file = storageDir.resolve(fname); + if (!file.getParent().equals(storageDir)) { + throw new BadRequestException("Invalid file name"); + } + try { + Files.createFile(file); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return file; + } + + /** + * Lookup an existing file in the storage. + * + * @param fname file name + * @return file + * @throws NotFoundException If the resolved file does not exist + * @throws BadRequestException if the resolved file is not contained in the storage directory + */ + public Path lookup(String fname) { + Path file = storageDir.resolve(fname); + if (!file.getParent().equals(storageDir)) { + throw new BadRequestException("Invalid file name"); + } + if (!Files.exists(file)) { + throw new NotFoundException("file not found"); + } + if (!Files.isRegularFile(file)) { + throw new BadRequestException("Not a file"); + } + return file; + } +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FtService.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FtService.java.mustache new file mode 100644 index 00000000000..f2baf666d2e --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/FtService.java.mustache @@ -0,0 +1,152 @@ +package {{package}}; + +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; + +import io.helidon.common.reactive.Single; +import io.helidon.faulttolerance.Async; +import io.helidon.faulttolerance.Bulkhead; +import io.helidon.faulttolerance.CircuitBreaker; +import io.helidon.faulttolerance.Fallback; +import io.helidon.faulttolerance.Retry; +import io.helidon.faulttolerance.Timeout; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +/** + * Simple service to demonstrate fault tolerance. + */ +public class FtService implements Service { + + private final Async async; + private final Bulkhead bulkhead; + private final CircuitBreaker breaker; + private final Fallback fallback; + private final Retry retry; + private final Timeout timeout; + + FtService() { + this.async = Async.create(); + this.bulkhead = Bulkhead.builder() + .queueLength(1) + .limit(1) + .name("helidon-example-bulkhead") + .build(); + this.breaker = CircuitBreaker.builder() + .volume(4) + .errorRatio(40) + .successThreshold(1) + .delay(Duration.ofSeconds(5)) + .build(); + this.fallback = Fallback.create(this::fallbackToMethod); + this.retry = Retry.builder() + .retryPolicy(Retry.DelayingRetryPolicy.noDelay(3)) + .build(); + this.timeout = Timeout.create(Duration.ofMillis(100)); + } + + @Override + public void update(Routing.Rules rules) { + rules.get("/async", this::asyncHandler) + .get("/bulkhead/{millis}", this::bulkheadHandler) + .get("/circuitBreaker/{success}", this::circuitBreakerHandler) + .get("/fallback/{success}", this::fallbackHandler) + .get("/retry/{count}", this::retryHandler) + .get("/timeout/{millis}", this::timeoutHandler); + } + + private void timeoutHandler(ServerRequest request, ServerResponse response) { + long sleep = Long.parseLong(request.path().param("millis")); + + timeout.invoke(() -> sleep(sleep)) + .thenAccept(response::send) + .exceptionally(response::send); + } + + private void retryHandler(ServerRequest request, ServerResponse response) { + int count = Integer.parseInt(request.path().param("count")); + + AtomicInteger call = new AtomicInteger(1); + AtomicInteger failures = new AtomicInteger(); + + retry.invoke(() -> { + int current = call.getAndIncrement(); + if (current < count) { + failures.incrementAndGet(); + return reactiveFailure(); + } + return Single.just("calls/failures: " + current + "/" + failures.get()); + }).thenAccept(response::send) + .exceptionally(response::send); + } + + private void fallbackHandler(ServerRequest request, ServerResponse response) { + boolean success = "true".equalsIgnoreCase(request.path().param("success")); + + if (success) { + fallback.invoke(this::reactiveData).thenAccept(response::send); + } else { + fallback.invoke(this::reactiveFailure).thenAccept(response::send); + } + } + + private void circuitBreakerHandler(ServerRequest request, ServerResponse response) { + boolean success = "true".equalsIgnoreCase(request.path().param("success")); + + if (success) { + breaker.invoke(this::reactiveData) + .thenAccept(response::send) + .exceptionally(response::send); + } else { + breaker.invoke(this::reactiveFailure) + .thenAccept(response::send) + .exceptionally(response::send); + } + + } + + private void bulkheadHandler(ServerRequest request, ServerResponse response) { + long sleep = Long.parseLong(request.path().param("millis")); + + bulkhead.invoke(() -> sleep(sleep)) + .thenAccept(response::send) + .exceptionally(response::send); + } + + private void asyncHandler(ServerRequest request, ServerResponse response) { + async.invoke(this::blockingData).thenApply(response::send); + } + + private Single reactiveFailure() { + return Single.error(new RuntimeException("reactive failure")); + } + + private Single sleep(long sleepMillis) { + return async.invoke(() -> { + try { + Thread.sleep(sleepMillis); + } catch (InterruptedException ignored) { + } + return "Slept for " + sleepMillis + " ms"; + }); + } + + private Single reactiveData() { + return async.invoke(this::blockingData); + } + + private String blockingData() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + return "blocked for 100 millis"; + } + + private Single fallbackToMethod(Throwable e) { + return Single.just("Failed back because of " + e.getMessage()); + } + +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache new file mode 100644 index 00000000000..16571f6ab8c --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache @@ -0,0 +1,28 @@ + +package {{package}}; + +public class Message { + + private String message; + + private String greeting; + + public Message() { + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + + public void setGreeting(String greeting) { + this.greeting = greeting; + } + + public String getGreeting() { + return this.greeting; + } +} \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache new file mode 100644 index 00000000000..977277ee2cf --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache @@ -0,0 +1,71 @@ +package {{package}}; + +import java.util.logging.Logger; + +{{#SimpleGreetService-imports}} +import {{.}}; +{{/SimpleGreetService-imports}} +import io.helidon.config.Config; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +/** + * A simple service to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/greet + * + * The message is returned as a JSON object + */ +public class SimpleGreetService implements Service { + + private static final Logger LOGGER = Logger.getLogger(SimpleGreetService.class.getName()); +{{#SimpleGreetResource-static-fields}} +{{.}} +{{/SimpleGreetResource-static-fields}} + + private final String greeting; + + SimpleGreetService(Config config) { + greeting = config.get("app.greeting").asString().orElse("Ciao"); + } + +{{#SimpleGreetResource-constructor}} +{{.}} +{{/SimpleGreetResource-constructor}} + + /** + * A service registers itself by updating the routing rules. + * + * @param rules the routing rules. + */ + @Override + public void update(Routing.Rules rules) { + rules.get("/", this::getDefaultMessageHandler); +{{#SimpleGreetResource-update}} +{{.}} +{{/SimpleGreetResource-update}} + } + + /** + * Return a worldly greeting message. + * + * @param request the server request + * @param response the server response + */ + private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { + String msg = String.format("%s %s!", greeting, "World"); + LOGGER.info("Greeting message is " + msg); + + Message message = new Message(); + message.setMessage(msg); + + response.send(message); + } + +{{#SimpleGreetService-methods}} +{{.}} +{{/SimpleGreetService-methods}} +} diff --git a/archetypes/helidon/src/main/archetype/se/bare/files/src/main/java/__pkg__/GreetService.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache similarity index 73% rename from archetypes/helidon/src/main/archetype/se/bare/files/src/main/java/__pkg__/GreetService.java.mustache rename to archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache index 4140eaff37d..1337d62c2d3 100644 --- a/archetypes/helidon/src/main/archetype/se/bare/files/src/main/java/__pkg__/GreetService.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache @@ -3,6 +3,9 @@ package {{package}}; import java.util.Collections; import java.util.logging.Logger; +{{#SimpleGreetService-imports}} +import {{.}}; +{{/SimpleGreetService-imports}} import jakarta.json.Json; import jakarta.json.JsonBuilderFactory; import jakarta.json.JsonObject; @@ -21,17 +24,24 @@ import io.helidon.webserver.Service; * * The message is returned as a JSON object */ -public class GreetService implements Service { +public class SimpleGreetService implements Service { - private static final Logger LOGGER = Logger.getLogger(GreetService.class.getName()); + private static final Logger LOGGER = Logger.getLogger(SimpleGreetService.class.getName()); private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); +{{#SimpleGreetResource-static-fields}} +{{.}} +{{/SimpleGreetResource-static-fields}} private final String greeting; - GreetService(Config config) { + SimpleGreetService(Config config) { greeting = config.get("app.greeting").asString().orElse("Ciao"); } +{{#SimpleGreetResource-constructor}} +{{.}} +{{/SimpleGreetResource-constructor}} + /** * A service registers itself by updating the routing rules. * @@ -40,6 +50,9 @@ public class GreetService implements Service { @Override public void update(Routing.Rules rules) { rules.get("/", this::getDefaultMessageHandler); +{{#SimpleGreetResource-update}} +{{.}} +{{/SimpleGreetResource-update}} } /** @@ -56,4 +69,8 @@ public class GreetService implements Service { .build(); response.send(returnObject); } + +{{#SimpleGreetService-methods}} +{{.}} +{{/SimpleGreetService-methods}} } diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache new file mode 100644 index 00000000000..ed5c9dc2029 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache @@ -0,0 +1,52 @@ +package {{package}}; + +import io.helidon.common.reactive.Single; +import io.helidon.config.Config; +import io.helidon.config.ConfigValue; +import io.helidon.media.jsonp.JsonpSupport; +import io.helidon.webclient.WebClient; + +public class WebClientMain { + + + private WebClientMain() { + } + + /** + * Executes WebClient examples. + * + * If no argument provided it will take server port from configuration server.port. + * + * User can override port from configuration by main method parameter with the specific port. + * + * @param args main method + */ + public static void main(String[] args) { + Config config = Config.create(); + String url; + ConfigValue port = config.get("server.port").asInt(); + if (!port.isPresent() || port.get() == -1) { + throw new IllegalStateException("Unknown port! Please specify port as a main method parameter " + + "or directly to config server.port"); + } + url = "http://localhost:" + port.get() + "/greet"; + + WebClient webClient = WebClient.builder() + .baseUri(url) + .config(config.get("client")) + .addMediaSupport(JsonpSupport.create()) + .build(); + + performGetMethod(webClient).await(); + } + + static Single performGetMethod(WebClient webClient) { + System.out.println("Get request execution."); + return webClient.get() + .request(String.class) + .peek(string -> { + System.out.println("GET request successfully executed."); + System.out.println(string); + }); + } +} diff --git a/archetypes/helidon/src/main/archetype/se/bare/files/application.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/application.config.yaml similarity index 100% rename from archetypes/helidon/src/main/archetype/se/bare/files/application.yaml rename to archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/application.config.yaml diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache new file mode 100644 index 00000000000..ce607e4eb12 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FileServiceTest.java.multipart.mustache @@ -0,0 +1,123 @@ + +package {{package}}; + +import io.helidon.common.http.MediaType; +import io.helidon.media.jsonp.JsonpSupport; +import io.helidon.media.multipart.FileFormParams; +import io.helidon.media.multipart.MultiPartSupport; +import io.helidon.webclient.WebClient; +import io.helidon.webclient.WebClientResponse; +import io.helidon.webserver.WebServer; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Tests {@link FileService}. + */ +@TestMethodOrder(OrderAnnotation.class) +public class FileServiceTest { + + private static WebServer webServer; + private static WebClient webClient; + + @BeforeAll + public static void startTheServer() { + webServer = Main.startServer().await(); + + webClient = WebClient.builder() + .baseUri("http://localhost:8080/multipart") + .addMediaSupport(MultiPartSupport.create()) + .addMediaSupport(JsonpSupport.create()) + .build(); + } + + @AfterAll + public static void stopServer() { + if (webServer != null) { + webServer.shutdown() + .await(10, TimeUnit.SECONDS); + } + } + + @Test + @Order(1) + public void testUpload() throws IOException { + Path file = Files.write( Files.createTempFile(null, null), "bar\n".getBytes(StandardCharsets.UTF_8)); + WebClientResponse response = webClient + .post() + .contentType(MediaType.MULTIPART_FORM_DATA) + .submit(FileFormParams.builder() + .addFile("file[]", "foo.txt", file) + .build()) + .await(); + assertThat(response.status().code(), is(200)); + } + + @Test + @Order(2) + public void testStreamUpload() throws IOException { + Path file = Files.write( Files.createTempFile(null, null), "stream bar\n".getBytes(StandardCharsets.UTF_8)); + Path file2 = Files.write( Files.createTempFile(null, null), "stream foo\n".getBytes(StandardCharsets.UTF_8)); + WebClientResponse response = webClient + .post() + .queryParam("stream", "true") + .contentType(MediaType.MULTIPART_FORM_DATA) + .submit(FileFormParams.builder() + .addFile("file[]", "streamed-foo.txt", file) + .addFile("otherPart", "streamed-foo2.txt", file2) + .build()) + .await(2, TimeUnit.SECONDS); + assertThat(response.status().code(), is(200)); + } + + @Test + @Order(3) + public void testList() { + WebClientResponse response = webClient + .get() + .contentType(MediaType.APPLICATION_JSON) + .request() + .await(); + assertThat(response.status().code(), Matchers.is(200)); + JsonObject json = response.content().as(JsonObject.class).await(); + assertThat(json, Matchers.is(notNullValue())); + List files = json.getJsonArray("files").getValuesAs(v -> ((JsonString) v).getString()); + assertThat(files, hasItem("foo.txt")); + } + + @Test + @Order(4) + public void testDownload() { + WebClientResponse response = webClient + .get() + .path("foo.txt") + .accept(MediaType.APPLICATION_OCTET_STREAM) + .request() + .await(); + assertThat(response.status().code(), is(200)); + assertThat(response.headers().first("Content-Disposition").orElse(null), + containsString("filename=\"foo.txt\"")); + byte[] bytes = response.content().as(byte[].class).await(); + assertThat(new String(bytes, StandardCharsets.UTF_8), Matchers.is("bar\n")); + } +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FtServiceTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FtServiceTest.java.mustache new file mode 100644 index 00000000000..6c32849ac78 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/FtServiceTest.java.mustache @@ -0,0 +1,184 @@ +package {{package}}; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import io.helidon.common.http.Http; +import io.helidon.webclient.WebClient; +import io.helidon.webclient.WebClientResponse; +import io.helidon.webserver.WebServer; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class FtServiceTest { + + private static WebServer server; + private static WebClient client; + + @BeforeAll + static void initClass() throws ExecutionException, InterruptedException { + server = Main.startServer() + .await(10, TimeUnit.SECONDS); + + client = WebClient.builder() + .baseUri("http://localhost:" + server.port() + "/ft") + .build(); + } + + @AfterAll + static void destroyClass() { + server.shutdown() + .await(5, TimeUnit.SECONDS); + } + + @Test + void testAsync() { + String response = client.get() + .path("/async") + .request(String.class) + .await(5, TimeUnit.SECONDS); + + assertThat(response, is("blocked for 100 millis")); + } + + @Test + void testBulkhead() throws InterruptedException { + // bulkhead is configured for limit of 1 and queue of 1, so third + // request should fail + client.get() + .path("/bulkhead/10000") + .request(); + + client.get() + .path("/bulkhead/10000") + .request(); + + // I want to make sure the above is connected + Thread.sleep(300); + + WebClientResponse third = client.get() + .path("/bulkhead/10000") + .request() + .await(1, TimeUnit.SECONDS); + + // registered an error handler in Main + assertThat(third.status(), is(Http.Status.SERVICE_UNAVAILABLE_503)); + assertThat(third.content().as(String.class).await(1, TimeUnit.SECONDS), is("bulkhead")); + } + + @Test + void testCircuitBreaker() { + String response = client.get() + .path("/circuitBreaker/true") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("blocked for 100 millis")); + + // error ratio is 20% within 10 request + client.get() + .path("/circuitBreaker/false") + .request() + .await(1, TimeUnit.SECONDS); + + // should work after first + response = client.get() + .path("/circuitBreaker/true") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("blocked for 100 millis")); + + // should open after second + client.get() + .path("/circuitBreaker/false") + .request() + .await(1, TimeUnit.SECONDS); + + WebClientResponse clientResponse = client.get() + .path("/circuitBreaker/true") + .request() + .await(1, TimeUnit.SECONDS); + response = clientResponse.content().as(String.class).await(1, TimeUnit.SECONDS); + + // registered an error handler in Main + assertThat(clientResponse.status(), is(Http.Status.SERVICE_UNAVAILABLE_503)); + assertThat(response, is("circuit breaker")); + } + + @Test + void testFallback() { + String response = client.get() + .path("/fallback/true") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("blocked for 100 millis")); + + response = client.get() + .path("/fallback/false") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("Failed back because of reactive failure")); + } + + @Test + void testRetry() { + String response = client.get() + .path("/retry/1") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("calls/failures: 1/0")); + + response = client.get() + .path("/retry/2") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("calls/failures: 2/1")); + + response = client.get() + .path("/retry/3") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("calls/failures: 3/2")); + + WebClientResponse clientResponse = client.get() + .path("/retry/4") + .request() + .await(1, TimeUnit.SECONDS); + + response = clientResponse.content().as(String.class).await(1, TimeUnit.SECONDS); + // no error handler specified + assertThat(clientResponse.status(), is(Http.Status.INTERNAL_SERVER_ERROR_500)); + assertThat(response, is("java.lang.RuntimeException: reactive failure")); + } + + @Test + void testTimeout() { + String response = client.get() + .path("/timeout/50") + .request(String.class) + .await(1, TimeUnit.SECONDS); + + assertThat(response, is("Slept for 50 ms")); + + WebClientResponse clientResponse = client.get() + .path("/timeout/105") + .request() + .await(1, TimeUnit.SECONDS); + + response = clientResponse.content().as(String.class).await(1, TimeUnit.SECONDS); + // error handler specified in Main + assertThat(clientResponse.status(), is(Http.Status.REQUEST_TIMEOUT_408)); + assertThat(response, is("timeout")); + } +} \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/TestCORS.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/TestCORS.java.mustache new file mode 100644 index 00000000000..2558e7a6004 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/TestCORS.java.mustache @@ -0,0 +1,150 @@ +package {{package}}; + +import io.helidon.common.http.Headers; +import io.helidon.config.Config; +import io.helidon.webclient.WebClient; +import io.helidon.webclient.WebClientRequestBuilder; +import io.helidon.webclient.WebClientResponse; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.cors.CrossOriginConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestCORS { + + private static WebClient client; + private static WebServer server; + + @BeforeAll + static void init() throws Exception { + server = Main.startServer().await(); + + client = WebClient.builder() + .baseUri("http://localhost:" + server.port()) + .build(); + + long timeout = 2000; // 2 seconds should be enough to start the server + long now = System.currentTimeMillis(); + + while (!server.isRunning()) { + Thread.sleep(100); + if ((System.currentTimeMillis() - now) > timeout) { + Assertions.fail("Failed to start webserver"); + } + } + } + + @AfterAll + static void cleanup() { + if (server != null) { + server.shutdown() + .await(10, TimeUnit.SECONDS); + } + } + + + @Test + void testAnonymousGreetWithCors() { + WebClientRequestBuilder builder = client.get(); + Headers headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + + WebClientResponse r = getResponse(builder); + assertThat("HTTP response", r.status().code(), is(200)); + String payload = fromPayload(r); + assertThat("HTTP response payload", payload, is("Hello World!")); + headers = r.headers(); + Optional allowOrigin = headers.value(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("Expected CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN + " is present", + allowOrigin.isPresent(), is(true)); + assertThat("CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin.get(), is("*")); + } + + @Test + void testCustomGreetingWithCors() { + + WebClientRequestBuilder builder = client.method("OPTIONS"); + Headers headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + headers.add("Access-Control-Request-Method", "PUT"); + + WebClientResponse r = builder.path("/cors") + .submit() + .await(); + + assertThat("pre-flight status", r.status().code(), is(200)); + Headers preflightResponseHeaders = r.headers(); + List allowMethods = preflightResponseHeaders.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS); + assertThat("pre-flight response check for " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS, + allowMethods, is(not(empty()))); + assertThat("Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_METHODS, allowMethods, contains("PUT")); + List allowOrigins = preflightResponseHeaders.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("pre-flight response check for " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, + allowOrigins, is(not(empty()))); + assertThat( "Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigins, contains("http://foo.com")); + + builder = client.put(); + headers = builder.headers(); + headers.add("Origin", "http://foo.com"); + headers.add("Host", "here.com"); + headers.addAll(preflightResponseHeaders); + + r = putResponse("Cheers", builder); + assertThat("HTTP response3", r.status().code(), is(200)); + headers = r.headers(); + allowOrigins = headers.values(CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN); + assertThat("Expected CORS header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, + allowOrigins, is(not(empty()))); + assertThat( "Header " + CrossOriginConfig.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigins, contains("http://foo.com")); + assertThat(fromPayload(r), containsString("Cheers World!")); + } + + @Test + void testGreetingChangeWithCorsAndOtherOrigin() { + WebClientRequestBuilder builder = client.put(); + Headers headers = builder.headers(); + headers.add("Origin", "http://other.com"); + headers.add("Host", "here.com"); + + WebClientResponse r = putResponse("Ahoy", builder); + boolean isOverriding = Config.create().get("cors").exists(); + assertThat("HTTP response3", r.status().code(), is(isOverriding ? 204 : 403)); + } + + + private static WebClientResponse getResponse(WebClientRequestBuilder builder) { + return builder + .path("/cors") + .submit() + .await(); + } + + private static String fromPayload(WebClientResponse response) { + return response + .content() + .as(String.class) + .await(); + } + + private static WebClientResponse putResponse(String message, WebClientRequestBuilder builder) { + return builder + .path("/cors/" + message) + .submit() + .await(); + } +} + diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache new file mode 100644 index 00000000000..d94286c7e1f --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache @@ -0,0 +1,43 @@ +package {{package}}; + +import io.helidon.webclient.WebClient; +import io.helidon.webserver.WebServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebClientMainTest { + + private static WebServer webServer; + private static WebClient webClient; + + @BeforeAll + public static void startTheServer() { + webServer = Main.startServer().await(); + webClient = WebClient.builder() + .baseUri("http://localhost:" + webServer.port() + "/simple-greet") + .build(); + } + + @AfterAll + public static void stopServer() throws Exception { + if (webServer != null) { + webServer.shutdown() + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); + } + } + + @Test + public void getSimpleGreetTest() throws Exception { + WebClientMain.performGetMethod(webClient) + .thenAccept(it -> assertThat(it, is("{\"message\":\"Hello World!\"}"))) + .await(); + } + +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/resources/META-INF/services/io.helidon.dbclient.spi.DbMapperProvider.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/resources/META-INF/services/io.helidon.dbclient.spi.DbMapperProvider.mustache new file mode 100644 index 00000000000..fb5c6768433 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/resources/META-INF/services/io.helidon.dbclient.spi.DbMapperProvider.mustache @@ -0,0 +1 @@ +{{package}}.DocumentMapperProvider \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/database/database-se.xml b/archetypes/helidon/src/main/archetype/se/database/database-se.xml index 8daf739ec43..5fde77d389b 100644 --- a/archetypes/helidon/src/main/archetype/se/database/database-se.xml +++ b/archetypes/helidon/src/main/archetype/se/database/database-se.xml @@ -20,180 +20,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://helidon.io/archetype/2.0 https://helidon.io/xsd/archetype-2.0.xsd"> - - + + + true + + Helidon SE Database - Helidon SE application that uses the dbclient API with an in-memory H2 database. - - - - - io.helidon.media - helidon-media-jsonp - - - io.helidon.media - helidon-media-jsonb - - - io.helidon.dbclient - helidon-dbclient-jsonp - - - io.helidon.dbclient - helidon-dbclient-health - - - io.helidon.dbclient - helidon-dbclient-metrics-jdbc - - - io.helidon.dbclient - helidon-dbclient-metrics - - - io.helidon.dbclient - helidon-dbclient-tracing - - - io.helidon.dbclient - helidon-dbclient-mongodb - - - io.helidon.dbclient - helidon-dbclient-jdbc - - - com.h2database - h2 - runtime - - - org.slf4j - slf4j-jdk14 - - - com.zaxxer - HikariCP - - - - io.helidon.dbclient.DbClient - io.helidon.dbclient.health.DbClientHealthCheck - io.helidon.media.jsonb.JsonbSupport - io.helidon.media.jsonp.JsonpSupport - - - - - - - - - - - - - - - System.out.println("Database here : http://localhost:" + ws.port() + "/pokemon"); - - java.util.Collections - jakarta.json.Json - jakarta.json.JsonBuilderFactory - jakarta.json.JsonArray - jakarta.json.JsonObject - - - io.helidon.media.jsonp.JsonpSupport - - - org.junit.jupiter.api.Order - - - - - - - - - - - - { - assertThat(array.size(), is(18)); - assertThat(array.get(0).asJsonObject().getInt("id"), is(1)); - assertThat(array.get(0).asJsonObject().getString("name"), is("Normal")); - }) - .toCompletableFuture() - .get(); - } - - @Test - public void testPokemons() throws Exception { - assertThat(getPokemonCount(), is(6)); - - webClient.get() - .path("/pokemon/1") - .request(JsonObject.class) - .thenAccept(pokemon -> assertThat(pokemon.getString("name"), is("Bulbasaur"))) - .toCompletableFuture() - .get(); - - webClient.get() - .path("/pokemon/name/Charmander") - .request(JsonObject.class) - .thenAccept(pokemon -> assertThat(pokemon.getJsonNumber("id_type").intValue(), is(10))) - .toCompletableFuture() - .get(); - - JsonObject json = JSON_BUILDER.createObjectBuilder() - .add("id", 100) - .add("idType", 1) - .add("name", "Test") - .build(); - webClient.post() - .path("/pokemon") - .submit(json) - .thenAccept(r -> assertThat(r.status(), is(Http.Status.OK_200))) - .toCompletableFuture() - .get(); - assertThat(getPokemonCount(), is(7)); - - webClient.delete() - .path("/pokemon/100") - .request() - .thenAccept(r -> assertThat(r.status(), is(Http.Status.OK_200))) - .toCompletableFuture() - .get(); - - assertThat(getPokemonCount(), is(6)); - } - - private int getPokemonCount() throws Exception { - CompletableFuture result = new CompletableFuture<>(); - webClient.get() - .path("/pokemon") - .request(JsonArray.class) - .thenAccept(array -> result.complete(array.size())); - return result.get(); - } - ]]> + diff --git a/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonMapperProvider.java.mustache b/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonMapperProvider.java.mustache index 958deffd1bd..ad9befd5ddb 100644 --- a/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonMapperProvider.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonMapperProvider.java.mustache @@ -36,10 +36,10 @@ public class PokemonMapperProvider implements DbMapperProvider { @Override public Pokemon read(DbRow row) { - DbColumn id = row.column("id"); - DbColumn name = row.column("name"); - DbColumn type = row.column("id_type"); - return new Pokemon(id.as(Integer.class), name.as(String.class), type.as(Integer.class)); + DbColumn id = row.column("{{id}}"); + DbColumn name = row.column("{{name}}"); + DbColumn type = row.column("{{id_type}}"); + return new Pokemon({{Pokemon-constructor-arg}}); } @Override diff --git a/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonTypeMapperProvider.java.mustache b/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonTypeMapperProvider.java.mustache index e463514afdf..1d4e6b1943e 100644 --- a/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonTypeMapperProvider.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/database/files/src/main/java/__pkg__/PokemonTypeMapperProvider.java.mustache @@ -1,3 +1,4 @@ + package {{package}}; import java.util.List; @@ -14,7 +15,7 @@ import io.helidon.dbclient.spi.DbMapperProvider; /** * Provider for PokemonType mappers. * - * Pokemon, and Pokemon character names, are trademarks of Nintendo. + * Pokemon, and Pokemon character names are trademarks of Nintendo. */ @Priority(1000) public class PokemonTypeMapperProvider implements DbMapperProvider { @@ -33,9 +34,9 @@ public class PokemonTypeMapperProvider implements DbMapperProvider { @Override public PokemonType read(DbRow row) { - DbColumn id = row.column("id"); - DbColumn name = row.column("name"); - return new PokemonType(id.as(Integer.class), name.as(String.class)); + DbColumn id = row.column("{{id}}"); + DbColumn name = row.column("{{name}}"); + return new PokemonType({{PokemonType-constructor-arg}}); } @Override diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.json.mustache b/archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.json.mustache new file mode 100644 index 00000000000..f4978bf59e2 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.json.mustache @@ -0,0 +1,112 @@ +package {{package}}; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; + +import io.helidon.webserver.Handler; +import io.helidon.common.http.Http; +import io.helidon.config.Config; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +/** + * A simple service to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/greet + * + * Get greeting message for Joe: + * curl -X GET http://localhost:8080/greet/Joe + * + * Change greeting + * curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting + * + * The message is returned as a JSON object + */ +public class GreetService implements Service { + + /** + * The config value for the key {@code greeting}. + */ + private final AtomicReference greeting = new AtomicReference<>(); + + private static final Logger LOGGER = Logger.getLogger(GreetService.class.getName()); + + GreetService(Config config) { + greeting.set(config.get("app.greeting").asString().orElse("Ciao")); + } + + /** + * A service registers itself by updating the routing rules. + * @param rules the routing rules. + */ + @Override + public void update(Routing.Rules rules) { + rules + .get("/", this::getDefaultMessageHandler) + .get("/{name}", this::getMessageHandler) + .put("/greeting", Handler.create(Message.class, this::updateGreeting)); + } + + /** + * Return a worldly greeting message. + * @param request the server request + * @param response the server response + */ + private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { + sendResponse(response, "World"); + } + + /** + * Return a greeting message using the name that was provided. + * @param request the server request + * @param response the server response + */ + private void getMessageHandler(ServerRequest request, ServerResponse response) { + String name = request.path().param("name"); + sendResponse(response, name); + } + + private void sendResponse(ServerResponse response, String name) { + String msg = String.format("%s %s!", greeting.get(), name); + + Message message = new Message(); + message.setMessage(msg); + response.send(message); + } + + private static T processErrors(Throwable ex, ServerRequest request, ServerResponse response) { + + LOGGER.log(Level.FINE, "Internal error", ex); + Message jsonError = new Message(); + jsonError.setMessage("Internal error"); + response.status(Http.Status.INTERNAL_SERVER_ERROR_500).send(jsonError); + + return null; + } + + /** + * Set the greeting to use in future messages. + * @param request the server request + * @param response the server response + * @param message the client message + */ + private void updateGreeting(ServerRequest request, + ServerResponse response, + Message message) { + + if (message.getGreeting() == null) { + Message jsonError = new Message(); + jsonError.setMessage("No greeting provided"); + response.status(Http.Status.BAD_REQUEST_400) + .send(jsonError); + return; + } + + greeting.set(message.getGreeting()); + response.status(Http.Status.NO_CONTENT_204).send(); + } +} diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.mustache b/archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.jsonp.mustache similarity index 100% rename from archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.mustache rename to archetypes/helidon/src/main/archetype/se/quickstart/files/src/main/java/__pkg__/GreetService.java.jsonp.mustache diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml index 5369011c492..d72c4ffaee7 100644 --- a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml +++ b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml @@ -1,7 +1,7 @@ + + + + + + java.util.Collections + jakarta.json.Json + jakarta.json.JsonBuilderFactory + jakarta.json.JsonObject + + + + + + + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/se.xml b/archetypes/helidon/src/main/archetype/se/se.xml index 2ab226702bf..2aad09595ae 100644 --- a/archetypes/helidon/src/main/archetype/se/se.xml +++ b/archetypes/helidon/src/main/archetype/se/se.xml @@ -22,10 +22,11 @@ - - + + @@ -44,4 +50,4 @@ ${app-type} - \ No newline at end of file + diff --git a/archetypes/legacy/bare-mp/pom.xml b/archetypes/legacy/bare-mp/pom.xml index 0cf25a88507..f750af2ad3c 100644 --- a/archetypes/legacy/bare-mp/pom.xml +++ b/archetypes/legacy/bare-mp/pom.xml @@ -31,6 +31,6 @@ mp - bare + custom diff --git a/archetypes/legacy/bare-se/pom.xml b/archetypes/legacy/bare-se/pom.xml index 5a4faa5bc56..5a3a365b583 100644 --- a/archetypes/legacy/bare-se/pom.xml +++ b/archetypes/legacy/bare-se/pom.xml @@ -31,6 +31,6 @@ se - bare + custom diff --git a/archetypes/legacy/pom.xml b/archetypes/legacy/pom.xml index 6aa87d05929..32d655ee92e 100644 --- a/archetypes/legacy/pom.xml +++ b/archetypes/legacy/pom.xml @@ -60,7 +60,7 @@ io.helidon.build-tools helidon-archetype-maven-plugin - false + false myproject diff --git a/etc/scripts/test-archetypes.sh b/etc/scripts/test-archetypes.sh new file mode 100755 index 00000000000..e98567d5710 --- /dev/null +++ b/etc/scripts/test-archetypes.sh @@ -0,0 +1,37 @@ +#!/bin/bash -ex +# +# 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. +# + +# 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 + +mvn ${MAVEN_ARGS} --version + +# Temporary workaround until job stages will share maven repository +mvn ${MAVEN_ARGS} -f ${WS_DIR}/pom.xml \ + install -e \ + -Dmaven.test.skip=true \ + -DskipTests \ + -Ppipeline + +cd ${WS_DIR}/archetypes +mvn ${MAVEN_ARGS} -e clean install diff --git a/pom.xml b/pom.xml index 70151feb972..6917f656ab3 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 1.6.0 3.0.0-M5 2.3 - 3.0.0-M5 + 3.0.0-RC1 ${version.lib.hibernate} 0.8.5 1.1.0 From 7df1e035e2b3c04e064e439906e0a4c0659ad977 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Thu, 14 Jul 2022 10:40:28 +0200 Subject: [PATCH 11/51] Bump Reactive Messaging API to 3.0-RC3 to allow CCR (#4535) Signed-off-by: Daniel Kec --- dependencies/pom.xml | 2 +- microprofile/tests/tck/tck-messaging/pom.xml | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 30ba34fd28e..465906f898d 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -111,7 +111,7 @@ 2.0 4.0 3.0 - 3.0-RC2 + 3.0-RC3 3.0-RC1 3.0-RC1 3.0 diff --git a/microprofile/tests/tck/tck-messaging/pom.xml b/microprofile/tests/tck/tck-messaging/pom.xml index 63c3ba031b1..ce1a5e08285 100644 --- a/microprofile/tests/tck/tck-messaging/pom.xml +++ b/microprofile/tests/tck/tck-messaging/pom.xml @@ -65,21 +65,6 @@ org.eclipse.microprofile.reactive.messaging:microprofile-reactive-messaging-tck - - - **/AsynchronousMessageProcessorAckTest.java - **/AsynchronousMessageSubscriberAckTest.java - **/AsynchronousPayloadProcessorAckTest.java - **/AsynchronousPayloadSubscriberAckTest.java - **/MessageProcessorAckTest.java - **/PayloadProcessorAckTest.java - **/PayloadSubscriberAckTest.java - **/DefaultOverflowStrategyOverflowTest.java - **/ThrowExceptionOverflowStrategyOverflowTest.java - **/EmitterOfMessageAcknowledgementTest.java - From 891dbaf5e7ee63cff7af516534d022e912d839e9 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Thu, 14 Jul 2022 15:04:25 +0300 Subject: [PATCH 12/51] [Now Doc PR] - MP Guides Menu and Overview inconsistencies #4509 (#4528) * Add missing cards to Helidon MP guides * Minor style fix * Fix comments * Fix rendering error --- docs/mp/guides/overview.adoc | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/mp/guides/overview.adoc b/docs/mp/guides/overview.adoc index ddf00f57f7c..54f9d0b25ef 100644 --- a/docs/mp/guides/overview.adoc +++ b/docs/mp/guides/overview.adoc @@ -97,6 +97,48 @@ in your Helidon MP application. -- Learn how to build a Helidon MicroProfile (MP) application from scratch. -- + +[CARD] +.Helidon MP Upgrade guide +[link=migration.adoc] +-- +Learn how to upgrade your Helidon MP application from 1.x to 2.x. +-- + +[CARD] +.OIDC Tutorial +[link=security-oidc.adoc] +-- +Learn how to set up OIDC security in your Helidon MP application. +-- + +[CARD] +.Helidon MP Tracing +[link=tracing.adoc] +-- +Learn how to use tracing in your Helidon MP application. +-- + +[CARD] +.Testing with JUnit 5 +[link=testing-junit5.adoc] +-- +Learn how to use JUnit5 for testing your applications. +-- + +[CARD] +.Helidon MP and JBatch +[link=jbatch.adoc] +-- +Learn how to use JBatch with Helidon MP. +-- + +[CARD] +.Performance tuning in Helidon MP +[link=performance-tuning.adoc] +-- +Learn how to improve performance of your application. +-- ==== == Build and Deploy @@ -144,7 +186,6 @@ Learn how to use Jib to create a container image without Docker. .Deploying to OKE [link=../../guides/oke.adoc] -- -Learn how to deploy your application to Oracle Cloud Infrastructure Container - Engine for Kubernetes (OKE). +Learn how to deploy your application to Oracle Cloud Infrastructure Container Engine for Kubernetes (OKE). -- ==== From 288bf7053d16376344f7ade5ff76494f79d18f68 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Thu, 14 Jul 2022 17:41:55 +0300 Subject: [PATCH 13/51] [New Doc PR] - Helidon SE Scheduling documentation #4312 (#4525) * SE Scheduling New Style Documentation initial commit * Fix copyright year * Fix comments Co-authored-by: Romain Grecourt --- docs/se/scheduling.adoc | 81 ++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/docs/se/scheduling.adoc b/docs/se/scheduling.adoc index 504f7b62aac..7c6f203ceda 100644 --- a/docs/se/scheduling.adoc +++ b/docs/se/scheduling.adoc @@ -21,10 +21,25 @@ :toc-placement: preamble :description: Scheduling in Helidon SE :keywords: helidon, se, scheduling +:h1Prefix: SE :feature-name: Scheduling :rootdir: {docdir}/.. -include::{rootdir}/includes/se.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Scheduling is an essential feature for the Enterprise. Helidon has its own implementation of Scheduling functionality +based on https://github.com/jmrozanec/cron-utils[Cron-utils]. + include::{rootdir}/includes/dependencies.adoc[] [source,xml] @@ -35,14 +50,13 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Scheduling -For scheduling periodic tasks it is possible to choose fixed rate setup or Cron expression. +== Usage +For scheduling periodic tasks, it is possible to choose a fixed rate or a Cron expression. === Fixed rate -For simple fixed rate invocation use . [source,java] -.Example of scheduling with fixed rate use `Scheduling.fixedRateBuilder()` builder. +.Scheduling with fixed rate use `Scheduling.fixedRateBuilder()` builder. ---- Scheduling.fixedRateBuilder() .delay(10) @@ -56,7 +70,7 @@ Metadata like human-readable interval description or configured values are avail FixedRateInvocation provided as task parameter. [source,java] -.Example with invocation metadata +.Invocation metadata ---- Scheduling.fixedRateBuilder() .delay(10) @@ -66,11 +80,11 @@ Scheduling.fixedRateBuilder() === Cron expression -For more complicated interval definition, cron expression can be leveraged with +For more complicated interval definition, Cron expression can be leveraged with `Scheduling.cronBuilder()` builder. [source,java] -.Example of scheduling with cron expression +.Scheduling with Cron expression ---- Scheduling.cronBuilder() .expression("0 15 8 ? * *") @@ -78,16 +92,57 @@ Scheduling.cronBuilder() .build(); ---- +== Configuration + +Configuration properties are added to `application.yaml` file: + +.Configuration properties +[width="90%",cols="3,10",frame="topbot",options="header"] +|==== +| Property | Description +| cron | String containing Cron setup +| concurrent | Boolean, equivalent `concurrentExecution` property of `@Scheduled`. Default `true`. +|==== + +=== Cron expression + +Cron expressions should be configured as follows. + include::{rootdir}/includes/cron.adoc[lines=19..] Metadata like human-readable interval description or configured values are available through CronInvocation provided as task parameter. +== Examples + +=== Fixed rate +For simple fixed rate invocation use . + +[source,java] +.Example of scheduling with fixed rate use `Scheduling.fixedRateBuilder()` builder. +---- +Scheduling.fixedRateBuilder() + .delay(10) + .initialDelay(5) + .timeUnit(TimeUnit.MINUTES) + .task(inv -> System.out.println("Every 10 minutes, first invocation 5 minutes after start")) + .build(); +---- + +Metadata like human-readable interval description or configured values are available through +`FixedRateInvocation` provided as task parameter. + + [source,java] .Example with invocation metadata ---- -Scheduling.cronBuilder() - .expression("0 15 8 ? * *") - .task(inv -> System.out.println("Method invoked " + inv.description())) - .build(); ----- \ No newline at end of file +Scheduling.fixedRateBuilder() + .delay(10) + .task(inv -> System.out.println("Method invoked " + inv.description())) + .build(); +---- + +== Reference + +* https://github.com/jmrozanec/cron-utils[Cron-utils GitHub page] +* link:{scheduling-javadoc-base-url}/io/helidon/microprofile/scheduling/package-summary.html[Helidon Scheduling JavaDoc] From 308ecbafb8f64670ee5f17992e299ed2e7eb1f70 Mon Sep 17 00:00:00 2001 From: Dmitry Kornilov Date: Thu, 14 Jul 2022 17:05:06 +0200 Subject: [PATCH 14/51] Spec URL and spec name fix (#4521) --- docs/mp/health.adoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/mp/health.adoc b/docs/mp/health.adoc index aa1a55bddc5..ac8899cef69 100644 --- a/docs/mp/health.adoc +++ b/docs/mp/health.adoc @@ -20,7 +20,6 @@ :spec-name: MicroProfile Health :description: {spec-name} support in Helidon MP :keywords: helidon, mp, microprofile, health -:health-release: {version.lib.microprofile-health} :feature-name: MicroProfile Health :microprofile-bundle: true :rootdir: {docdir}/.. @@ -91,8 +90,7 @@ To enable built-in health checks add the following dependency == Usage -Helidon MP Health implements MicroProfile Health Checks -link:http://download.eclipse.org/microprofile/microprofile-health-{health-release}/microprofile-health-spec.pdf[Specification]. +Helidon implements link:{microprofile-health-spec-url}[MicroProfile Health] Specification. The spec prescribes how external tools probe a service's health checks and how you implement health checks as part of your microservice that are specific to your service's needs. From 9accea860a148705de984579c60ae6c5da81a69e Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Thu, 14 Jul 2022 17:06:17 +0200 Subject: [PATCH 15/51] Routing 3 doc update (#4506) * Routing 3 doc update Signed-off-by: Daniel Kec --- docs/se/webserver.adoc | 34 +++++++++++++++++++ docs/se/websocket.adoc | 74 +++++++++++++++++++++++++++++++----------- 2 files changed, 89 insertions(+), 19 deletions(-) diff --git a/docs/se/webserver.adoc b/docs/se/webserver.adoc index a9dd4c480f0..21cecdd9f0d 100644 --- a/docs/se/webserver.adoc +++ b/docs/se/webserver.adoc @@ -31,7 +31,9 @@ include::{rootdir}/includes/se.adoc[] - <> - <> - <> +- <> - <> +- <> - <> - <> - <> @@ -299,6 +301,22 @@ To complete the request handling, you must send a response by calling the `res.s <1> handler that terminates the request handling for any HTTP method using the `/hello` path <2> send the response +== Protocol Specific Routing +Handling routes based on the protocol version is possible by registering specific routes +on routing builder. + +[source,java] +.Routing based on HTTP version +---- +.routing(r -> r + .get("/any-version", (req, res) -> res.send("HTTP Version " + req.version())) + .route(Http1Route.route(GET, "/version-specific", (req, res) -> res.send("HTTP/1.1 route"))) + .route(Http2Route.route(GET, "/version-specific", (req, res) -> res.send("HTTP/2 route"))) +) +---- + +While `Http1Route` for Http/1 is always available with Helidon webserver, +other routes like `Http2Route` for <> needs to be added as additional dependency. == Error Handling @@ -377,6 +395,22 @@ exception called `req.next()`, then the exception is translated to an HTTP respo * Otherwise, the exceptions are translated to an Internal Server Error HTTP error code `500`. +== Http/2 Support + +Helidon supports Http/2 upgrade from Http/1, Http/2 without prior knowledge +and Http/2 with ALPN over TLS. +Http/2 support is enabled in webserver by default when it's artefact is available on classpath. + +=== Maven Coordinates +To enable Http/2 support add the following dependency to your project's `pom.xml`. + +[source,xml] +---- + + io.helidon.webserver + helidon-webserver-http2 + +---- == Static Content Support diff --git a/docs/se/websocket.adoc b/docs/se/websocket.adoc index 8a076ae6e87..a96871284e6 100644 --- a/docs/se/websocket.adoc +++ b/docs/se/websocket.adoc @@ -21,10 +21,19 @@ :keywords: helidon, webserver, websocket, se :rootdir: {docdir}/.. + include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> + +== Overview Helidon integrates with link:https://projects.eclipse.org/projects/ee4j.tyrus[Tyrus] to provide support for the - {jakarta-websocket-spec-url}[Jakarta WebSocket API]. +{jakarta-websocket-spec-url}[Jakarta WebSocket API]. The WebSocket API enables Java applications to participate in WebSocket interactions as both servers and clients. The server API supports two flavors: annotated and programmatic endpoints. @@ -36,11 +45,21 @@ more flexible since they allow different method signatures depending on the application needs, whereas programmatic endpoints must implement an interface and are, therefore, bounded to its definition. -Helidon SE support is based on the `TyrusSupport` class which is -akin to `JerseySupport`, and enables Helidon application to -defined both annotated and programmatic WebSocket endpoints. +Helidon SE support is based on the `WebSocketRouting` class which enables Helidon application to +configure routing for both annotated and programmatic WebSocket endpoints. + + +include::{rootdir}/includes/dependencies.adoc[] -== Helidon SE Example +[source,xml] +---- + + io.helidon.webserver + helidon-webserver-websocket + +---- + +== Example This section describes the implementation of a simple application that uses a REST resource to push messages into a shared queue and a @@ -64,8 +83,12 @@ public class MessageQueueService implements Service { } private void handlePost(ServerRequest request, ServerResponse response) { - request.content().as(String.class).thenAccept(messageQueue::push); - response.status(204).send(); + request.content() + .as(String.class) + .thenAccept(it -> { + messageQueue.push(it); + response.status(204).send(); + }); } } ---- @@ -89,6 +112,7 @@ public class MessageBoardEndpoint extends Endpoint { @Override public void onMessage(String message) { try { + // Send all messages in the queue if (message.equals("SEND")) { while (!messageQueue.isEmpty()) { session.getBasicRemote().sendObject(messageQueue.pop()); @@ -114,20 +138,27 @@ the web server. This is accomplished via a `Routing` builder: [source,java] ---- - List> encoders = - Collections.singletonList(UppercaseEncoder.class); - - Routing.builder() - .register("/rest", new MessageQueueService()) - .register("/websocket", - TyrusSupport.builder().register( - ServerEndpointConfig.Builder.create( - MessageBoardEndpoint.class, "/board").encoders( - encoders).build()).build()) - .build(); +List> encoders = + Collections.singletonList(UppercaseEncoder.class); + +WebServer server = WebServer.builder() + .port(8080) + .routing(r -> r + .register("/web", StaticContentSupport.builder("/WEB") + .welcomeFileName("index.html") + .build()) + .register("/rest", new MessageQueueService()) + ) + .addRouting(WebSocketRouting.builder() + .endpoint("/websocket", ServerEndpointConfig.Builder.create(MessageBoardEndpoint.class, "/board") + .encoders(encoders) + .build()) + .build() + ) + .build() ---- -This code snippet uses multiple builders for `Routing`, `TyrusSupport` and `ServerEndpointConfig`. +This code snippet uses multiple builders for `Routing`, `WebSocketRouting` and `ServerEndpointConfig`. In particular, it registers `MessageBoardEndpoint.class` at `"/websocket/board"` and associates with it a _message encoder_. For more information on message encoders and decoders the reader see the {jakarta-websocket-spec-url}[websocket specification]; in this @@ -138,3 +169,8 @@ Endpoint methods in Helidon SE are executed in Netty's worker thread pool. Threa pool are intended to be _non-blocking_, thus it is recommended for any blocking or long-running operation triggered by an endpoint method to be executed using a separate thread pool. See the documentation for `io.helidon.common.configurable.ThreadPoolSupplier`. + +== Reference + +* link:{javadoc-base-url}/io.helidon.webserver.websocket/module-summary.html[Helidon WebSocket JavaDoc] +* link:{jakarta-websocket-spec-url}[Jakarta WebSocket Specification] From 0242aeca8dbe779948ca4c8e1a5f98956030f75d Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Thu, 14 Jul 2022 17:09:41 +0200 Subject: [PATCH 16/51] MP Reactive Messaging 3.0 docs (#4469) Signed-off-by: Daniel Kec --- docs/images/msg/channel.svg | 658 ++++++++++++++++++++ docs/images/msg/connector-config.svg | 371 +++++++++++ docs/images/msg/processor.svg | 437 +++++++++++++ docs/includes/attributes.adoc | 1 + docs/mp/reactivemessaging/connector.adoc | 130 ---- docs/mp/reactivemessaging/introduction.adoc | 404 +++++++++++- docs/mp/reactivemessaging/kafka.adoc | 79 ++- docs/mp/reactivemessaging/message.adoc | 153 ----- docs/mp/reactivemessaging/mock.adoc | 140 +++++ docs/se/reactivemessaging/connector.adoc | 2 +- docs/se/reactivemessaging/introduction.adoc | 4 +- docs/sitegen.yaml | 3 +- 12 files changed, 2061 insertions(+), 321 deletions(-) create mode 100644 docs/images/msg/channel.svg create mode 100644 docs/images/msg/connector-config.svg create mode 100644 docs/images/msg/processor.svg delete mode 100644 docs/mp/reactivemessaging/connector.adoc delete mode 100644 docs/mp/reactivemessaging/message.adoc create mode 100644 docs/mp/reactivemessaging/mock.adoc diff --git a/docs/images/msg/channel.svg b/docs/images/msg/channel.svg new file mode 100644 index 00000000000..ad8f97882b9 --- /dev/null +++ b/docs/images/msg/channel.svg @@ -0,0 +1,658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @Channel("my-channel") + @Inject@Channel("my-channel")Emitter<String> emitter; + @Outgoing("my-channel")public PublisherBuilder<String> produceMessages() { return ReactiveStreams.of("foo", "bar", "baz");} + @ApplicationScoped@Connector("example-connector")class ExampleConnector implements IncomingConnectorFactory { @Override public PublisherBuilder<? extends Message<?>> getPublisherBuilder(Config c) { return ReactiveStreams.of("foo", "bar") .map(Message::of); } + OR + OR + + + + @Incoming("my-channel")public void printMessage(String msg) { System.out.println("Just received message: " + msg);} + @Inject@Channel("my-channel")Publisher<String> field; + @ApplicationScoped@Connector("example-connector")public class ExampleConnector implements OutgoingConnectorFactory { @Override public SubscriberBuilder<? extends Message<?>, Void> getSubscriberBuilder(Config c) { return ReactiveStreams.<Message<?>>builder() .map(Message::getPayload) .forEach(o -> System.out.println("Connector says: " + o)); } + + + + OR + OR + + + UPSTREAM + DOWNSTREAM + + 1 + + 3 + + 4 + + 2 + + 6 + + 5 + + diff --git a/docs/images/msg/connector-config.svg b/docs/images/msg/connector-config.svg new file mode 100644 index 00000000000..7ce962b6461 --- /dev/null +++ b/docs/images/msg/connector-config.svg @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + mp.messaging: incoming.from-connector-channel: connector: example-connector channel-specific-prop: foo connector-specific-prop: bar channel-name: from-connector-channel outgoing.to-connector-channel: connector: example-connector channel-specific-prop: foo connector-specific-prop: bar channel-name: to-connector-channel connector.example-connector: connector-specific-prop: bar + @ApplicationScoped@Connector("example-connector")public class ExampleConnector implements IncomingConnectorFactory { @Override public PublisherBuilder<? extends Message<?>> getPublisherBuilder(final Config config) { + + + + + + + + + + + 2 + + + + 1 + + + diff --git a/docs/images/msg/processor.svg b/docs/images/msg/processor.svg new file mode 100644 index 00000000000..01b5f722659 --- /dev/null +++ b/docs/images/msg/processor.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @Channel("my-channel") + + + + + 1 + + + + 3 + + + + 2 + + + + + + + + + + + + @Channel("other-channel") + + + @Incoming("my-channel") @Outgoing("other-channel") String mapToUpperCase(String payload) { return payload.toUpperCase(); } + + diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index edcbfcb0665..80c273ac2c4 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -203,6 +203,7 @@ endif::[] :eclipselink-doc-base-url: https://www.eclipse.org/eclipselink/documentation/{version-lib-exclipselink} :jib-base-url: https://github.com/GoogleContainerTools/jib/blob/v{version-plugin-jib}-maven/jib-maven-plugin :jedis-base-url: {javadoc-io-base-url}/redis.clients/jedis/{version-lib-jedis}/redis/clients +:kafka-client-base-url: https://kafka.apache.org/28/documentation.html :oci-sdk-config-url: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm#SDK_and_CLI_Configuration_File :oci-database-url: https://docs.oracle.com/en-us/iaas/Content/Database/home.htm diff --git a/docs/mp/reactivemessaging/connector.adoc b/docs/mp/reactivemessaging/connector.adoc deleted file mode 100644 index 24dfaf3ebda..00000000000 --- a/docs/mp/reactivemessaging/connector.adoc +++ /dev/null @@ -1,130 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Connector -:toc: -:toc-placement: preamble -:description: Reactive Messaging Connector in Helidon MP -:keywords: helidon, mp, messaging, connector -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -== Messaging Connector Bean - -Messaging connector is just an application scoped bean which implements -`IncomingConnectorFactory`, `OutgoingConnectorFactory` or both. - -[source,java] -.Example connector `example-connector`: ----- -@ApplicationScoped -@Connector("example-connector") -public class ExampleConnector implements IncomingConnectorFactory, OutgoingConnectorFactory { - - @Override - public PublisherBuilder> getPublisherBuilder(Config config) { - return ReactiveStreams.of("foo", "bar") - .map(Message::of); - } - - @Override - public SubscriberBuilder, Void> getSubscriberBuilder(Config config) { - return ReactiveStreams.>builder() - .map(Message::getPayload) - .forEach(o -> System.out.println("Connector says: " + o)); - } -} ----- - -[source,yaml] -.Example of channel to connector mapping config: ----- -mp.messaging.outgoing.to-connector-channel.connector: example-connector -mp.messaging.incoming.from-connector-channel.connector: example-connector ----- - -[source,java] -.Example producing to connector: ----- -@Outgoing("to-connector-channel") -public Publisher produce() { - return Flowable.just("fee", "fie"); -} - -> Connector says: fee -> Connector says: fie ----- - - -[source,java] -.Example consuming from connector: ----- -@Incoming("from-connector-channel") -public void consume(String value) { - System.out.println("Consuming: " + value); -} - -> Consuming: foo -> Consuming: bar ----- - -=== Configuration - -[source,java] -.Example connector accessing configuration: ----- -@ApplicationScoped -@Connector("example-connector") -public class ExampleConnector implements IncomingConnectorFactory { - - @Override - public PublisherBuilder> getPublisherBuilder(final Config config) { - - String firstPropValue = config.getValue("first-test-prop", String.class); // <1> - String secondPropValue = config.getValue("second-test-prop", String.class); - - return ReactiveStreams.of(firstPropValue, secondPropValue) - .map(Message::of); - } -} ----- -<1> Config context is merged from channel and connector contexts - -[source,yaml] -.Example of channel to connector mapping config with custom properties: ----- -mp.messaging.incoming.from-connector-channel.connector: example-connector # <1> -mp.messaging.incoming.from-connector-channel.first-test-prop: foo # <2> -mp.messaging.connector.example-connector.second-test-prop: bar # <3> ----- -<1> Channel -> Connector mapping -<2> Channel configuration properties -<3> Connector configuration properties - -[source,java] -.Example consuming from connector: ----- -@Incoming("from-connector-channel") -public void consume(String value) { - System.out.println("Consuming: " + value); -} - -> Consuming: foo -> Consuming: bar ----- diff --git a/docs/mp/reactivemessaging/introduction.adoc b/docs/mp/reactivemessaging/introduction.adoc index 4e48812339b..d56f659a6b0 100644 --- a/docs/mp/reactivemessaging/introduction.adoc +++ b/docs/mp/reactivemessaging/introduction.adoc @@ -17,15 +17,42 @@ /////////////////////////////////////////////////////////////////////////////// = Reactive Messaging -:toc: -:toc-placement: preamble :spec-name: MicroProfile Reactive Messaging :description: {spec-name} support in Helidon MP :keywords: helidon, mp, microprofile, messaging +:messaging-release: {version.lib.microprofile-reactive-messaging-api} :feature-name: MicroProfile Reactive Messaging :microprofile-bundle: false :rootdir: {docdir}/../.. +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview +There is more to expect from the modern messaging than we got from old-fashioned Message Driven Beans, +blocking is not always favorable way to apply backpressure to the message source, actually is not the only way anymore. +Reactive messaging uses reactive streams as message channels, users can construct very effective pipelines for working +with the messages, or use messaging in the old-fashioned way. Similarly to the MDB's +link:{microprofile-reactive-messaging-spec-url}[MicroProfile Reactive Messaging] +uses CDI beans to produce, consume or process messages over Reactive Streams. +Such messaging bean is expected to be either in `ApplicationScoped` or `Dependent` scope. +Messages are managed by methods annotated by `@Incoming` and `@Outgoing` +and the invocation is always driven by message core - either at assembly time, or for every message coming from the stream. + include::{rootdir}/includes/mp.adoc[] include::{rootdir}/includes/dependencies.adoc[] @@ -47,36 +74,24 @@ To include health checks for Messaging add the following dependency: ---- -== Reactive Messaging +== Channel -link:{microprofile-reactive-messaging-spec-url}[MicroProfile Reactive Messaging] -uses CDI beans to produce, consume or process messages over Reactive Streams. -Such messaging bean is expected to be either in `ApplicationScoped` or `Dependent` scope. -Messages are managed by methods annotated by `@Incoming` and `@Outgoing` -and the invocation is always driven by message core - either at assembly time, or for every message coming from the stream. - -WARNING: Messaging methods are not meant to be invoked directly! - -[[terms]] -.Terms definition -|=== -|link:{microprofile-reactive-messaging-spec-url}#_supported_method_signatures[messaging method]| bean method invoked by messaging Specification -|link:{microprofile-reactive-messaging-spec-url}#_connector[connector]| Reactive Messaging connector -|link:{microprofile-reactive-messaging-spec-url}#_channel[channel]| named pair of producer and consumer, both sides can be either messaging method or connector -|=== +Reactive messaging uses named channels to connect always one source(upstream) with one consumer(downstream). +Each channel needs to have both ends connected otherwise container won't successfully start. -The bean can have methods annotated by -link:{microprofile-reactive-messaging-spec-url}#_message_consumption_with_incoming[`@Incoming`], -link:{microprofile-reactive-messaging-spec-url}#_message_production_with_outgoing[`@Outgoing`] or both. +image::msg/channel.svg[Messaging Channel] -=== Consuming methods with `@Incoming` annotation +Channels can be connected either to <> (1), <> (2) or <> (3) on the upstream side. And <> (4), <> (5) or <> (6) +on the downstream. -The annotation has one required attribute `value` that defines the channel name. +=== Consuming method +Consuming methods can be connected to the channel's downstream to consume the message coming through the channel. +Incoming annotation has one required attribute `value` that defines the channel name. -Such annotated <> can function in two ways: +Consuming method can function in two ways: -* consume every message coming from the stream connected to the <> -* prepare reactive stream's subscriber and connect it to the channel +* consume every message coming from the stream connected to the <> - invoked per each message +* prepare reactive stream's subscriber and connect it to the channel - invoked only once during the channel construction [source,java] .Example consuming every message from channel `example-channel-2`: @@ -98,7 +113,33 @@ public Subscriber printMessage() { } ---- -=== Producing methods with `@Outgoing` annotation +=== Injected publisher +Directly injected publisher can be connected as a channel downstream, +we can consume the data from the channel by subscribing to it. + +Helidon can inject following types of publishers: + + * `Publisher` - Reactive streams publisher with unwrapped payload + * `Publisher>` - Reactive streams publisher with whole message + * `PublisherBuilder` - MP Reactive streams operators publisher builder with unwrapped payload + * `PublisherBuilder>` - MP Reactive streams operators publisher builder with whole message + * `Flow.Publisher` - JDK's flow publisher with unwrapped payload + * `Flow.Publisher>` - JDK's flow publisher with whole message + * `Multi` - Helidon flow reactive operators with unwrapped payload + * `Multi>` - Helidon flow reactive operators with whole message + +[source,java] +.Example of consuming payloads from channel `example-channel-1` with injected publisher: +---- +@Inject +public MyBean(@Channel("example-channel-1") Multi multiChannel) { + multiChannel + .map(String::toUpperCase) + .forEach(s -> System.out.println("Received " + s)); +} +---- + +=== Producing method The annotation has one required attribute `value` that defines the link:{microprofile-reactive-messaging-spec-url}#_channel[channel] name. @@ -126,12 +167,54 @@ public Publisher printMessage() { } ---- -=== Processing methods with `@Incoming` and `@Outgoing` annotation +WARNING: Messaging methods are not meant to be invoked directly! + +=== Emitter +For sending messages from imperative code we can inject special channel source called emitter. +Emitter can serve only as an upstream, source of the messages, for messaging channel. + +[source,java] +.Example of sending message from JAX-RS method to channel `example-channel-1` +---- +@Inject +@Channel("example-channel-1") +private Emitter emitter; + +@PUT +@Path("/sendMessage") +@Consumes(MediaType.TEXT_PLAIN) +public Response sendMessage(final String payload) { + emitter.send(payload); +} +---- + +Emitters as a source of messages for reactive channels needs to address possible backpressure from the downstream side +of the channel. In case there is not enough demand from the downstream we can configure best fitting strategy for our use-case +with annotation `@OnOverflow`. + +.Overflow strategies +|=== +|Strategy |Description +|BUFFER |Buffer unconsumed values until configured bufferSize is reached, when reached calling `Emitter.emit` throws `IllegalStateException`. Buffer size can be configured with `@OnOverflow` or with config key `mp.messaging.emitter.default-buffer-size`. Default value is `128`. +|UNBOUNDED_BUFFER |Buffer unconsumed values until application runs out of memory. +|THROW_EXCEPTION |Calling `Emitter.emit` throws `IllegalStateException` if there is not enough items requested by downstream. +|DROP |If there is not enough items requested by downstream, emitted message is silently dropped. +|FAIL |If there is not enough items requested by downstream, emitting message causes error signal being send to downstream. Whole channel is terminated. No other messages can be sent. +|LATEST |Keeps only the latest item. Any previous unconsumed message is silently dropped. +|NONE |Messages are sent to downstream even if there is no demand. Backpressure is effectively ignored. +|=== + +=== Processing method Such link:{microprofile-reactive-messaging-spec-url}#_method_consuming_and_producing[methods] acts as processors, consuming messages from one channel and producing to another. -Such annotated <> can function in multiple ways: +image::msg/processor.svg[Processor method connecting two channels together] + +Diagram shows how processing method (2) serves as a downstream to the `my-channel` (1) and an upstream to the `other-channel` (3), +connecting them together. + +Processing method can function in multiple ways: * process every message * prepare reactive stream's processor and connect it between the channels @@ -169,6 +252,254 @@ public String processMessage(String msg) { } ---- +=== Connector +Messaging connector is an application scoped bean which implements one or both of following interfaces: + +* `IncomingConnectorFactory` - connector can create an upstream publisher to produce messages to a channel +* `OutgoingConnectorFactory` - connector can create a downstream subscriber to consume messages from a channel + +[source,java] +.Example connector `example-connector`: +---- +@ApplicationScoped +@Connector("example-connector") +public class ExampleConnector implements IncomingConnectorFactory, OutgoingConnectorFactory { + + @Override + public PublisherBuilder> getPublisherBuilder(Config config) { + return ReactiveStreams.of("foo", "bar") + .map(Message::of); + } + + @Override + public SubscriberBuilder, Void> getSubscriberBuilder(Config config) { + return ReactiveStreams.>builder() + .map(Message::getPayload) + .forEach(o -> System.out.println("Connector says: " + o)); + } +} +---- + +Channel needs to be instructed to use connector as its upstream or downstream by configuration. + +[source,yaml] +.Example of channel to connector mapping config: +---- +mp.messaging.outgoing.to-connector-channel.connector: example-connector #<1> +mp.messaging.incoming.from-connector-channel.connector: example-connector #<2> +---- + +<1> Use connector `example-connector` as a downstream for channel `to-connector-channel` to consume the messages from the channel +<2> Use connector `example-connector` as an upstream for channel `to-connector-channel` to produce messages to the channel + +[source,java] +.Example producing to connector: +---- +@Outgoing("to-connector-channel") +public Publisher produce() { + return Flowable.just("fee", "fie"); +} + +> Connector says: fee +> Connector says: fie +---- + + +[source,java] +.Example consuming from connector: +---- +@Incoming("from-connector-channel") +public void consume(String value) { + System.out.println("Consuming: " + value); +} + +> Consuming: foo +> Consuming: bar +---- + +When connector constructs publisher or subscriber for given channel, +it can access general connector configuration and channel specific properties merged together with +special synthetic property `channel-name`. + +image::msg/connector-config.svg[Connector config] + +Connector specific config (1) merged together with global connector config (2). + +[source,java] +.Example connector accessing configuration: +---- +@ApplicationScoped +@Connector("example-connector") +public class ExampleConnector implements IncomingConnectorFactory { + + @Override + public PublisherBuilder> getPublisherBuilder(final Config config) { + + String firstPropValue = config.getValue("channel-specific-prop", String.class); //<1> + String secondPropValue = config.getValue("connector-specific-prop", String.class); + String secondPropValue = config.getValue("channel-name", String.class); //<2> + + return ReactiveStreams.of(firstPropValue, secondPropValue) + .map(Message::of); + } +} +---- +<1> Config context is merged from channel and connector contexts +<2> Name of the channel requesting publisher as it's upstream from this connector + +[source,yaml] +.Example of channel to connector mapping config with custom properties: +---- +mp.messaging.incoming.from-connector-channel.connector: example-connector<1> +mp.messaging.incoming.from-connector-channel.channel-specific-prop: foo<2> +mp.messaging.connector.example-connector.connector-specific-prop: bar<3> +---- +<1> Channel -> Connector mapping +<2> Channel configuration properties +<3> Connector configuration properties + +[source,java] +.Example consuming from connector: +---- +@Incoming("from-connector-channel") +public void consume(String value) { + System.out.println("Consuming: " + value); +} + +> Consuming: foo +> Consuming: bar +---- + +=== Message +The Reactive Messaging link:{microprofile-reactive-messaging-spec-url}#_message[Message] +class can be used to wrap or unwrap data items between methods and connectors. +The message wrapping and unwrapping can be performed explicitly by using +`org.eclipse.microprofile.reactive.messaging.Message#of(T)` or implicitly through the messaging core. + +[source,java] +.Example of explicit and implicit wrapping and unwrapping +---- +@Outgoing("publisher-payload") +public PublisherBuilder streamOfMessages() { + return ReactiveStreams.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +} + +@Incoming("publisher-payload") +@Outgoing("wrapped-message") +public Message rewrapMessageManually(Message message) { + return Message.of(Integer.toString(message.getPayload())); +} + +@Incoming("wrapped-message") +public void consumeImplicitlyUnwrappedMessage(String value) { + System.out.println("Consuming message: " + value); +} +---- + +=== Acknowledgement +Message carries a callback for reception acknowledgement(ack) and negative acknowledgement(nack), +acknowledgement in messaging methods is possible manually by +`org.eclipse.microprofile.reactive.messaging.Message#ack` or automatically according explicit +or implicit acknowledgement strategy by messaging core. Explicit strategy configuration is possible +with `@Acknowledgment` annotation which has one required attribute `value` that expects the strategy type from enum +`org.eclipse.microprofile.reactive.messaging.Acknowledgment.Strategy`. More information about supported signatures +and implicit automatic acknowledgement can be found in specification +link:{microprofile-reactive-messaging-spec-url}#_message_acknowledgement[Message acknowledgement]. + +[[terms]] +.Acknowledgement strategies +|=== +|`@Acknowledgment(Acknowledgment.Strategy.NONE)`| No acknowledgment +|`@Acknowledgment(Acknowledgment.Strategy.MANUAL)`| No automatic acknowledgment +|`@Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING)`| Ack automatically before method invocation or processing +|`@Acknowledgment(Acknowledgment.Strategy.POST_PROCESSING)`| Ack automatically after method invocation or processing +|=== + +[source,java] +.Example of manual acknowledgment +---- +@Outgoing("consume-and-ack") +public PublisherBuilder streamOfMessages() { + return ReactiveStreams.of(Message.of("This is Payload", () -> { + System.out.println("This particular message was acked!"); + return CompletableFuture.completedFuture(null); + })).buildRs(); +} + +@Incoming("consume-and-ack") +@Acknowledgment(Acknowledgment.Strategy.MANUAL) +public CompletionStage receiveAndAckMessage(Message msg) { + return msg.ack(); //<1> +} +---- +<1> Calling ack() will print "This particular message was acked!" to System.out + +[source,java] +.Example of manual acknowledgment +---- +@Outgoing("consume-and-ack") +public PublisherBuilder streamOfMessages() { + return ReactiveStreams.of(Message.of("This is Payload", () -> { + System.out.println("This particular message was acked!"); + return CompletableFuture.completedFuture(null); + })).buildRs(); +} + +@Incoming("consume-and-ack") +@Acknowledgment(Acknowledgment.Strategy.MANUAL) +public CompletionStage receiveAndAckMessage(Message msg) { + return msg.ack(); //<1> +} +---- +<1> Calling ack() will print "This particular message was acked!" to System.out + +[source,java] +.Example of explicit pre-process acknowledgment +---- +@Outgoing("consume-and-ack") +public PublisherBuilder streamOfMessages() { + return ReactiveStreams.of(Message.of("This is Payload", () -> { + System.out.println("This particular message was acked!"); + return CompletableFuture.completedFuture(null); + })).buildRs(); +} + +/** +* Prints to the console: +* > This particular message was acked! +* > Method invocation! +*/ +@Incoming("consume-and-ack") +@Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) +public CompletionStage receiveAndAckMessage(Message msg) { + System.out.println("Method invocation!"); + return CompletableFuture.completedFuture(null); +} +---- +[source,java] +.Example of explicit post-process acknowledgment +---- +@Outgoing("consume-and-ack") +public PublisherBuilder streamOfMessages() { + return ReactiveStreams.of(Message.of("This is Payload", () -> { + System.out.println("This particular message was acked!"); + return CompletableFuture.completedFuture(null); + })).buildRs(); +} + +/** +* Prints to the console: +* > Method invocation! +* > This particular message was acked! +*/ +@Incoming("consume-and-ack") +@Acknowledgment(Acknowledgment.Strategy.POST_PROCESSING) +public CompletionStage receiveAndAckMessage(Message msg) { + System.out.println("Method invocation!"); + return CompletableFuture.completedFuture(null); +} +---- + === Health check Messaging in Helidon has built in health probes for liveness and readiness. To activate it @@ -191,4 +522,17 @@ every messaging channel to have its own probe. "my-channel-2": "UP" } } ----- \ No newline at end of file +---- + +WARNING: Due to the nack support are exceptions thrown in messaging methods NOT translated to error and cancel signals implicitly anymore + +== Upgrading to Messaging 3.0 +.Non-backward compatible changes: +* Exceptions thrown in messaging methods are not propagated as error or cancel signals to the stream(use `mp.messaging.helidon.propagate-errors=true` for backward compatible mode) - errors are propagated only to the upstream by `nack` functionality. +* Default acknowledgement strategies changed for selected signatures(all with Message as a parameter or return type) - See the specification issue link:{https://github.com/eclipse/microprofile-reactive-messaging/pull/97}[#97] + +== Reference + +* link:https://helidon.io/docs/v2/apidocs/io.helidon.microprofile.messaging/module-summary.html[Helidon MicroProfile Reactive Messaging] +* link:{microprofile-reactive-messaging-spec-url}[MicroProfile Reactive Messaging Specification] +* link:https://github.com/eclipse/microprofile-reactive-messaging[MicroProfile Reactive Messaging on GitHub] \ No newline at end of file diff --git a/docs/mp/reactivemessaging/kafka.adoc b/docs/mp/reactivemessaging/kafka.adoc index 754aa4b0fa0..e95a6e5445f 100644 --- a/docs/mp/reactivemessaging/kafka.adoc +++ b/docs/mp/reactivemessaging/kafka.adoc @@ -25,6 +25,11 @@ :microprofile-bundle: false :rootdir: {docdir}/../.. +== ToC + +- <> +- <> + include::{rootdir}/includes/mp.adoc[] include::{rootdir}/includes/dependencies.adoc[] @@ -36,8 +41,10 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Reactive Kafka Connector +== Overview Connecting streams to Kafka with Reactive Messaging couldn't be easier. +There is a standard Kafka client behind the scenes, all the link:{kafka-client-base-url}#producerconfigs[producer] and link:{kafka-client-base-url}#consumerconfigs[consumer] configs can +be propagated through messaging config. [source,yaml] .Example of connector config: @@ -47,7 +54,7 @@ mp.messaging: incoming.from-kafka: connector: helidon-kafka topic: messaging-test-topic-1 - auto.offset.reset: latest + auto.offset.reset: latest # <1> enable.auto.commit: true group.id: example-group-id @@ -57,13 +64,16 @@ mp.messaging: connector: helidon-kafka: - bootstrap.servers: localhost:9092 + bootstrap.servers: localhost:9092 # <2> key.serializer: org.apache.kafka.common.serialization.StringSerializer value.serializer: org.apache.kafka.common.serialization.StringSerializer key.deserializer: org.apache.kafka.common.serialization.StringDeserializer value.deserializer: org.apache.kafka.common.serialization.StringDeserializer ---- +<1> Kafka client consumer's property auto.offset.reset configuration for `from-kafka` channel only +<2> Kafka client's property link:{kafka-client-base-url}#consumerconfigs_bootstrap.servers[bootstrap.servers] configuration for all channels using the connector + [source,java] .Example of consuming from Kafka: ---- @@ -83,6 +93,69 @@ public PublisherBuilder produceToKafka() { } ---- +== NACK Strategy + +|=== +| Strategy | Description +| Kill channel | Nacked message sends error signal and causes channel failure so Messaging Health check can report it as DOWN +| DLQ | Nacked messages are sent to specified dead-letter-queue +| Log only | Nacked message is logged and channel continues normally +|=== + +=== Kill channel +Default NACK strategy for Kafka connector. When + +=== Dead Letter Queue + +Sends nacked messages to error topic, link:https://en.wikipedia.org/wiki/Dead_letter_queue[DLQ] is well known pattern for dealing with unprocessed messages. + +Helidon can derive connection settings for DLQ topic automatically if the error +topic is present on the same Kafka cluster. +Serializers are derived from deserializers used for consumption +`org.apache.kafka.common.serialization.StringDeserializer` > +`org.apache.kafka.common.serialization.StringSerializer`. +In this ideal case only the name of error topic is needed. + +[source,yaml] +.Example of derived DLQ config: +---- +mp.messaging: + incoming: + my-channel: + nack-dlq: dql_topic_name +---- + +In case custom connection is needed, all the usual producer configuration is possible +under the `nack-dlq` key. + +[source,yaml] +.Example of custom DLQ config: +---- +mp.messaging: + incoming: + my-channel: + nack-dlq: + topic: dql_topic_name + bootstrap.servers: localhost:9092 + key.serializer: org.apache.kafka.common.serialization.StringSerializer + value.serializer: org.apache.kafka.common.serialization.StringSerializer + +---- + +=== Log only + +Only logs nacked messages and throws them away, offset is committed and channel +continues normally consuming subsequent messages. + +[source,yaml] +.Example of log only enabled nack strategy +---- +mp.messaging: + incoming: + my-channel: + nack-log-only: true +---- + Don't forget to check out the examples with pre-configured Kafka docker image, for easy testing: * {helidon-github-tree-url}/examples/messaging \ No newline at end of file diff --git a/docs/mp/reactivemessaging/message.adoc b/docs/mp/reactivemessaging/message.adoc deleted file mode 100644 index 9cf177f6ea5..00000000000 --- a/docs/mp/reactivemessaging/message.adoc +++ /dev/null @@ -1,153 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Message -:toc: -:toc-placement: preamble -:description: Reactive Messaging Message in Helidon MP -:keywords: helidon, mp, messaging, message -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -== Message -The Reactive Messaging link:{microprofile-reactive-messaging-spec-url}#_message[Message] -class can be used to wrap or unwrap data items between methods and connectors. -The message wrapping and unwrapping can be performed explicitly by using -`org.eclipse.microprofile.reactive.messaging.Message#of(T)` or implicitly through the messaging core. - -[source,java] -.Example of explicit and implicit wrapping and unwrapping ----- -@Outgoing("publisher-payload") -public PublisherBuilder streamOfMessages() { - return ReactiveStreams.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); -} - -@Incoming("publisher-payload") -@Outgoing("wrapped-message") -public Message rewrapMessageManually(Message message) { - return Message.of(Integer.toString(message.getPayload())); -} - -@Incoming("wrapped-message") -public void consumeImplicitlyUnwrappedMessage(String value) { - System.out.println("Consuming message: " + value); -} ----- - -=== Acknowledgement -Message carries a callback for reception acknowledgement, acknowledgement in messaging methods is possible manually by -`org.eclipse.microprofile.reactive.messaging.Message#ack` or automatically according explicit -or implicit acknowledgement strategy by messaging core. Explicit strategy configuration is possible -with `@Acknowledgment` annotation which has one required attribute `value` that expects the strategy type from enum -`org.eclipse.microprofile.reactive.messaging.Acknowledgment.Strategy`. More information about supported signatures -and implicit automatic acknowledgement can be found in specification -link:{microprofile-reactive-messaging-spec-url}#_message_acknowledgement[Message acknowledgement]. - -[[terms]] -.Acknowledgement strategies -|=== -|`@Acknowledgment(Acknowledgment.Strategy.NONE)`| No acknowledgment -|`@Acknowledgment(Acknowledgment.Strategy.MANUAL)`| No automatic acknowledgment -|`@Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING)`| Ack automatically before method invocation or processing -|`@Acknowledgment(Acknowledgment.Strategy.POST_PROCESSING)`| Ack automatically after method invocation or processing -|=== - -[source,java] -.Example of manual acknowledgment ----- -@Outgoing("consume-and-ack") -public PublisherBuilder streamOfMessages() { - return ReactiveStreams.of(Message.of("This is Payload", () -> { - System.out.println("This particular message was acked!"); - return CompletableFuture.completedFuture(null); - })).buildRs(); -} - -@Incoming("consume-and-ack") -@Acknowledgment(Acknowledgment.Strategy.MANUAL) -public void receiveAndAckMessage(Message msg) { - msg.ack();<1> -} ----- -<1> Calling ack() will print "This particular message was acked!" to System.out - -[source,java] -.Example of manual acknowledgment ----- -@Outgoing("consume-and-ack") -public PublisherBuilder streamOfMessages() { - return ReactiveStreams.of(Message.of("This is Payload", () -> { - System.out.println("This particular message was acked!"); - return CompletableFuture.completedFuture(null); - })).buildRs(); -} - -@Incoming("consume-and-ack") -@Acknowledgment(Acknowledgment.Strategy.MANUAL) -public void receiveAndAckMessage(Message msg) { - msg.ack();<1> -} ----- -<1> Calling ack() will print "This particular message was acked!" to System.out - -[source,java] -.Example of explicit pre-process acknowledgment ----- -@Outgoing("consume-and-ack") -public PublisherBuilder streamOfMessages() { - return ReactiveStreams.of(Message.of("This is Payload", () -> { - System.out.println("This particular message was acked!"); - return CompletableFuture.completedFuture(null); - })).buildRs(); -} - -/** -* Prints to the console: -* > This particular message was acked! -* > Method invocation! -*/ -@Incoming("consume-and-ack") -@Acknowledgment(Acknowledgment.Strategy.PRE_PROCESSING) -public void receiveAndAckMessage(Message msg) { - System.out.println("Method invocation!"); -} ----- -[source,java] -.Example of explicit post-rocess acknowledgment ----- -@Outgoing("consume-and-ack") -public PublisherBuilder streamOfMessages() { - return ReactiveStreams.of(Message.of("This is Payload", () -> { - System.out.println("This particular message was acked!"); - return CompletableFuture.completedFuture(null); - })).buildRs(); -} - -/** -* Prints to the console: -* > Method invocation! -* > This particular message was acked! -*/ -@Incoming("consume-and-ack") -@Acknowledgment(Acknowledgment.Strategy.POST_PROCESSING) -public void receiveAndAckMessage(Message msg) { - System.out.println("Method invocation!"); -} ----- diff --git a/docs/mp/reactivemessaging/mock.adoc b/docs/mp/reactivemessaging/mock.adoc new file mode 100644 index 00000000000..b7ab6ee4ca1 --- /dev/null +++ b/docs/mp/reactivemessaging/mock.adoc @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += Mock Connector +:toc: +:toc-placement: preamble +:description: Reactive Messaging Mock connector for testing +:keywords: helidon, mp, messaging, test, mock +:feature-name: Mock Connector +:microprofile-bundle: false +:rootdir: {docdir}/../.. + +== ToC + +- <> +- <> +- <> +- <> +- <> + +== Overview +Mock connector is a simple application scoped bean that can be used for emitting to a channel +or asserting received data in a test environment. All data received are kept in memory only. + +include::{rootdir}/includes/mp.adoc[] +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.messaging.mock + helidon-messaging-mock + +---- + +WARNING: Mock connector should be used in the test environment only! + +For injecting Mock Connector use `@TestConnector` qualifier: +[source,java] +---- +@Inject +@TestConnector +MockConnector mockConnector; +---- + +=== Emitting data + +.Emitting String values `a`, `b`, `c` +[source,java] +---- +mockConnector + .incoming("my-incoming-channel", String.class) // <1> + .emit("a", "b", "c"); +---- +<1> Get incoming channel of given name and payload type + +=== Asserting data + +.Awaiting and asserting payloads with custom mapper +[source,java] +---- +mockConnector + .outgoing("my-outgoing-channel", String.class) // <1> + .awaitData(TIMEOUT, Message::getPayload, "a", "b", "c"); // <2> +---- +<1> Get outgoing channel of given name and payload type +<2> Request number of expected items and block the thread until items arrive then assert the payloads + +=== Configuration +|=== +|Key |Default value | Description +|mock-data | | Initial data emitted to the channel immediately after subscription +|mock-data-type |java.lang.String| Type of the emitted initial data to be emitted +|=== + +== Helidon test with Mock Connector +Mock connector works great with built-in Helidon test support for +link:/{rootdir}/testing/testing.adoc[JUnit 5] or link:/{rootdir}/testing/testing-ng.adoc[Test NG]. + +As Helidon test support makes a bean out of your test, you can inject MockConnector right in to it. + +[source,java] +---- +@HelidonTest +@DisableDiscovery // <1> +@AddBean(MockConnector.class) // <2> +@AddExtension(MessagingCdiExtension.class) // <3> +@AddConfig(key = "mp.messaging.incoming.test-channel-in.connector", value = MockConnector.CONNECTOR_NAME) // <4> +@AddConfig(key = "mp.messaging.incoming.test-channel-in.mock-data-type", value = "java.lang.Integer") // <5> +@AddConfig(key = "mp.messaging.incoming.test-channel-in.mock-data", value = "6,7,8") // <6> +@AddConfig(key = "mp.messaging.outgoing.test-channel-out.connector", value = MockConnector.CONNECTOR_NAME) // <7> +public class MessagingTest { + + private static final Duration TIMEOUT = Duration.ofSeconds(15); + + @Inject + @TestConnector + private MockConnector mockConnector; // <8> + + @Incoming("test-channel-in") + @Outgoing("test-channel-out") + int multiply(int payload) { // <9> + return payload * 10; + } + + @Test + void testMultiplyChannel() { + mockConnector.outgoing("test-channel-out", Integer.TYPE) // <10> + .awaitPayloads(TIMEOUT, 60, 70, 80); + } +} +---- + +<1> If you want to add all the beans manually +<2> Manually add MockConnector bean, so it is accessible by messaging for constructing the channels +<3> Messaging support in Helidon MP is provided by this CDI extension +<4> Instruct messaging to use `mock-connector` as an upstream for channel `test-channel-in` +<5> Generate mock data of `java.lang.Integer`, String is default +<6> Generate mock data +<7> Instruct messaging to use `mock-connector` as a downstream for channel `test-channel-out` +<8> Inject mock connector so we can access publishers and subscribers registered within the mock connector +<9> Messaging processing method connecting together channels `test-channel-in` and `test-channel-out` +<10> Actual JUnit 5 test method which is going to block the thread until 3 items are intercepted on `test-channel-out` +channel's downstream and assert those with expected values. + diff --git a/docs/se/reactivemessaging/connector.adoc b/docs/se/reactivemessaging/connector.adoc index 60c960bf62d..0532aa669d2 100644 --- a/docs/se/reactivemessaging/connector.adoc +++ b/docs/se/reactivemessaging/connector.adoc @@ -203,4 +203,4 @@ As the API is the same for xref:../../mp/reactivemessaging/introduction.adoc[Mic `@ApplicationScoped`. Such connector is treated as a bean in Helidon MP. For specific information about creating messaging connectors for Helidon MP visit -xref:../../mp/reactivemessaging/connector.adoc[Messaging Connector Bean]. +xref:../../mp/reactivemessaging/introduction.adoc#Connector[Messaging Connector Bean]. diff --git a/docs/se/reactivemessaging/introduction.adoc b/docs/se/reactivemessaging/introduction.adoc index 108b4e0e17c..eda6319e83a 100644 --- a/docs/se/reactivemessaging/introduction.adoc +++ b/docs/se/reactivemessaging/introduction.adoc @@ -110,7 +110,7 @@ Messaging.builder() === Message Reactive Messaging in Helidon SE uses the same concept of -xref:../../mp/reactivemessaging/message.adoc[message wrapping] as MicroProfile messaging. +message wrapping as MicroProfile messaging. The only notable difference is that SE Messaging does almost no implicit or automatic acknowledgement due to _no magic_ philosophy of Helidon SE. @@ -121,7 +121,7 @@ implicit ack before callback is executed is necessary. === Connector Connector concept is a way for connecting <> to external sources. -To make xref:connector.adoc[creation and usage of connectors] +To make creation and usage of connectors as easy and versatile as possible, Helidon SE Messaging uses same API for connectors like xref:../../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging] does. This allows connectors to be usable in both flavors of Helidon with one limitation which is diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 309b7e13a7d..60bee52a7ec 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -463,11 +463,10 @@ backend: value: "message" sources: - "introduction.adoc" - - "message.adoc" - - "connector.adoc" - "kafka.adoc" - "jms.adoc" - "aq.adoc" + - "mock.adoc" - type: "PAGE" title: "REST Client" source: "restclient.adoc" From 5bd7d76d430535b4aa8cf49e6ed0cf52f335dade Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 14 Jul 2022 19:24:16 +0200 Subject: [PATCH 17/51] New config docs (#4541) * Refactored config documentation: * move to docs/config * fixed wrong annotations * added section with all generated docs * use _ instead of . in file names (could not render menu) * sorted options Signed-off-by: Tomas Langer --- ...icroprofile.openapi.MPOpenAPIBuilder.adoc} | 41 +++-- ...pi.internal.OpenAPIConfigImpl.Builder.adoc | 56 ++++++ ...on.tracing.jaeger.JaegerTracerBuilder.adoc | 67 +++++++ ...helidon_common_configurable_LruCache.adoc} | 19 +- ...helidon_common_configurable_Resource.adoc} | 27 +-- ...igurable_ScheduledThreadPoolSupplier.adoc} | 23 ++- ...mmon_configurable_ThreadPoolSupplier.adoc} | 35 ++-- .../io_helidon_common_pki_KeyConfig.adoc} | 23 ++- ...common_pki_KeyConfig_KeystoreBuilder.adoc} | 35 ++-- ...idon_common_pki_KeyConfig_PemBuilder.adoc} | 23 ++- .../io_helidon_faulttolerance_Bulkhead.adoc | 56 ++++++ ...helidon_faulttolerance_CircuitBreaker.adoc | 58 +++++++ .../io_helidon_faulttolerance_Retry.adoc | 57 ++++++ ...lttolerance_Retry_DelayingRetryPolicy.adoc | 51 ++++++ ...aulttolerance_Retry_JitterRetryPolicy.adoc | 53 ++++++ .../io_helidon_faulttolerance_Timeout.adoc | 53 ++++++ .../io_helidon_health_HealthSupport.adoc} | 36 ++-- ...io_helidon_media_common_MediaContext.adoc} | 17 +- ...lidon_metrics_api_BaseMetricsSettings.adoc | 56 ++++++ ..._metrics_api_ComponentMetricsSettings.adoc | 55 ++++++ ...eyPerformanceIndicatorMetricsSettings.adoc | 56 ++++++ ...o_helidon_metrics_api_MetricsSettings.adoc | 60 +++++++ ...on_metrics_api_RegistryFilterSettings.adoc | 50 ++++++ ..._helidon_metrics_api_RegistrySettings.adoc | 56 ++++++ ...microprofile_openapi_MPOpenAPISupport.adoc | 70 ++++++++ .../io_helidon_openapi_OpenAPISupport.adoc | 53 ++++++ .../io_helidon_openapi_SEOpenAPISupport.adoc} | 37 ++-- docs/config/io_helidon_security_Security.adoc | 77 ++++++++ .../io_helidon_security_SecurityTime.adoc | 60 +++++++ ..._security_providers_abac_AbacProvider.adoc | 63 +++++++ ...urity_providers_common_OutboundConfig.adoc | 51 ++++++ ...urity_providers_common_OutboundTarget.adoc | 87 ++++++++++ ...s_httpauth_ConfigUserStore_ConfigUser.adoc | 51 ++++++ ...viders_httpauth_HttpBasicAuthProvider.adoc | 68 ++++++++ ..._security_providers_oidc_OidcProvider.adoc | 164 ++++++++++++++++++ ...rity_providers_oidc_common_OidcConfig.adoc | 146 ++++++++++++++++ ...io_helidon_security_util_TokenHandler.adoc | 52 ++++++ ...common_rest_HelidonRestServiceSupport.adoc | 51 ++++++ ...ervicecommon_rest_RestServiceSettings.adoc | 51 ++++++ docs/config/io_helidon_tracing_Tracer.adoc | 67 +++++++ .../io_helidon_tracing_TracerBuilder.adoc | 65 +++++++ ...elidon_webserver_SocketConfiguration.adoc} | 64 ++++--- ...iguration_SocketConfigurationBuilder.adoc} | 62 ++++--- .../io_helidon_webserver_WebServer.adoc} | 82 +++++---- .../io_helidon_webserver_WebServerTls.adoc} | 31 ++-- ...don_webserver_cors_CrossOriginConfig.adoc} | 29 ++-- ...io_smallrye_openapi_api_OpenApiConfig.adoc | 56 ++++++ docs/mp/health.adoc | 2 +- docs/mp/openapi.adoc | 2 +- docs/se/webserver.adoc | 4 +- docs/sitegen.yaml | 50 ++++++ .../java/io/helidon/faulttolerance/Retry.java | 2 +- .../java/io/helidon/health/HealthSupport.java | 2 +- .../helidon/metrics/api/MetricsSettings.java | 4 +- .../helidon/metrics/api/RegistrySettings.java | 3 +- 55 files changed, 2420 insertions(+), 249 deletions(-) rename docs/{includes/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc => config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc} (77%) create mode 100644 docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc create mode 100644 docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc rename docs/{includes/config/io.helidon.common.configurable.LruCache.adoc => config/io_helidon_common_configurable_LruCache.adoc} (77%) rename docs/{includes/config/io.helidon.common.configurable.Resource.adoc => config/io_helidon_common_configurable_Resource.adoc} (82%) rename docs/{includes/config/io.helidon.common.configurable.ScheduledThreadPoolSupplier.adoc => config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc} (78%) rename docs/{includes/config/io.helidon.common.configurable.ThreadPoolSupplier.adoc => config/io_helidon_common_configurable_ThreadPoolSupplier.adoc} (85%) rename docs/{includes/config/io.helidon.common.pki.KeyConfig.adoc => config/io_helidon_common_pki_KeyConfig.adoc} (62%) rename docs/{includes/config/io.helidon.common.pki.KeyConfig.KeystoreBuilder.adoc => config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc} (80%) rename docs/{includes/config/io.helidon.common.pki.KeyConfig.PemBuilder.adoc => config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc} (65%) create mode 100644 docs/config/io_helidon_faulttolerance_Bulkhead.adoc create mode 100644 docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc create mode 100644 docs/config/io_helidon_faulttolerance_Retry.adoc create mode 100644 docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc create mode 100644 docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc create mode 100644 docs/config/io_helidon_faulttolerance_Timeout.adoc rename docs/{includes/config/io.helidon.health.HealthSupport.adoc => config/io_helidon_health_HealthSupport.adoc} (75%) rename docs/{includes/config/io.helidon.media.common.MediaContext.adoc => config/io_helidon_media_common_MediaContext.adoc} (84%) create mode 100644 docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc create mode 100644 docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc create mode 100644 docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc create mode 100644 docs/config/io_helidon_metrics_api_MetricsSettings.adoc create mode 100644 docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc create mode 100644 docs/config/io_helidon_metrics_api_RegistrySettings.adoc create mode 100644 docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc create mode 100644 docs/config/io_helidon_openapi_OpenAPISupport.adoc rename docs/{includes/config/io.helidon.openapi.SEOpenAPISupport.adoc => config/io_helidon_openapi_SEOpenAPISupport.adoc} (81%) create mode 100644 docs/config/io_helidon_security_Security.adoc create mode 100644 docs/config/io_helidon_security_SecurityTime.adoc create mode 100644 docs/config/io_helidon_security_providers_abac_AbacProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_common_OutboundConfig.adoc create mode 100644 docs/config/io_helidon_security_providers_common_OutboundTarget.adoc create mode 100644 docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc create mode 100644 docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc create mode 100644 docs/config/io_helidon_security_util_TokenHandler.adoc create mode 100644 docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc create mode 100644 docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc create mode 100644 docs/config/io_helidon_tracing_Tracer.adoc create mode 100644 docs/config/io_helidon_tracing_TracerBuilder.adoc rename docs/{includes/config/io.helidon.webserver.SocketConfiguration.adoc => config/io_helidon_webserver_SocketConfiguration.adoc} (81%) rename docs/{includes/config/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder.adoc => config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc} (79%) rename docs/{includes/config/io.helidon.webserver.WebServer.adoc => config/io_helidon_webserver_WebServer.adoc} (80%) rename docs/{includes/config/io.helidon.webserver.WebServerTls.adoc => config/io_helidon_webserver_WebServerTls.adoc} (74%) rename docs/{includes/config/io.helidon.webserver.cors.CrossOriginConfig.adoc => config/io_helidon_webserver_cors_CrossOriginConfig.adoc} (82%) create mode 100644 docs/config/io_smallrye_openapi_api_OpenApiConfig.adoc diff --git a/docs/includes/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc b/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc similarity index 77% rename from docs/includes/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc rename to docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc index 99140802fda..f37f56240ac 100644 --- a/docs/includes/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc +++ b/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc @@ -16,44 +16,55 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.microprofile.openapi.MPOpenAPISupport :keywords: helidon, config, io.helidon.microprofile.openapi.MPOpenAPISupport :basic-table-intro: The table below lists the configuration keys that configure io.helidon.microprofile.openapi.MPOpenAPISupport +include::{rootdir}/includes/attributes.adoc[] + += MPOpenAPISupport (microprofile.openapi) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io.helidon.microprofile.openapi.MPOpenAPISupport[io.helidon.microprofile.openapi.MPOpenAPISupport] + [source,text] -.Type +.Config key ---- -io.helidon.microprofile.openapi.MPOpenAPISupport +mp.openapi ---- -==== Configuration options - +== Configuration options Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`scan.exclude.classes` |string[] |{nbsp} |Specify the list of classes to exclude from scans. +|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. +|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. +|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. |`scan.classes` |string[] |{nbsp} |Specify the list of classes to scan. |`scan.disable` |boolean |`false` |Disable annotation scanning. +|`scan.exclude.classes` |string[] |{nbsp} |Specify the list of classes to exclude from scans. |`scan.exclude.packages` |string[] |{nbsp} |Specify the list of packages to exclude from scans. |`scan.packages` |string[] |{nbsp} |Specify the list of packages to scan. -|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. -|`cors` |link:../../shared/config/io.helidon.webserver.cors.CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. -|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. -|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. |`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. -|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. -|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. -|`servers.path.{path}` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. |`servers` |string[] |{nbsp} |Sets servers. -|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. +|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. +|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc b/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc new file mode 100644 index 00000000000..670867d10c7 --- /dev/null +++ b/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.smallrye.openapi.api.OpenApiConfig +:keywords: helidon, config, io.smallrye.openapi.api.OpenApiConfig +:basic-table-intro: The table below lists the configuration keys that configure io.smallrye.openapi.api.OpenApiConfig +include::{rootdir}/includes/attributes.adoc[] + += io.smallrye.openapi.api.OpenApiConfig Configuration + +// tag::config[] + + +Type: io.smallrye.openapi.api.OpenApiConfig + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. +|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. +|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. +|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. +|`servers` |string[] |{nbsp} |Sets servers. +|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. +|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc b/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc new file mode 100644 index 00000000000..404836eda57 --- /dev/null +++ b/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.tracing.Tracer +:keywords: helidon, config, io.helidon.tracing.Tracer +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.tracing.Tracer +include::{rootdir}/includes/attributes.adoc[] + += Tracer (tracing) Configuration + +// tag::config[] + +Jaeger tracer configuration. + + +Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.Tracer[io.helidon.tracing.Tracer] + + +This is a standalone configuration type, prefix from configuration root: `tracing` + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should + use a no-op tracer. +|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. +|`host` |string |{nbsp} |Host to use to connect to tracing collector. + Default is defined by each tracing integration. +|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. +|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. + Default is defined by each tracing integration. +|`port` |int |{nbsp} |Port to use to connect to tracing collector. + Default is defined by each tracing integration. +|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. + Default is defined by each tracing integration. +|`service` |string |{nbsp} |Service name of the traced service. +|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.configurable.LruCache.adoc b/docs/config/io_helidon_common_configurable_LruCache.adoc similarity index 77% rename from docs/includes/config/io.helidon.common.configurable.LruCache.adoc rename to docs/config/io_helidon_common_configurable_LruCache.adoc index dd9edb53648..ec9a75e7551 100644 --- a/docs/includes/config/io.helidon.common.configurable.LruCache.adoc +++ b/docs/config/io_helidon_common_configurable_LruCache.adoc @@ -16,25 +16,28 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.configurable.LruCache :keywords: helidon, config, io.helidon.common.configurable.LruCache :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.configurable.LruCache +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.configurable.LruCache ----- += LruCache (common.configurable) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.LruCache[io.helidon.common.configurable.LruCache] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description @@ -42,3 +45,5 @@ Optional configuration options: |`capacity` |int |`10000` |Configure capacity of the cache. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.configurable.Resource.adoc b/docs/config/io_helidon_common_configurable_Resource.adoc similarity index 82% rename from docs/includes/config/io.helidon.common.configurable.Resource.adoc rename to docs/config/io_helidon_common_configurable_Resource.adoc index 2e9821e3664..f9b2b0513a3 100644 --- a/docs/includes/config/io.helidon.common.configurable.Resource.adoc +++ b/docs/config/io_helidon_common_configurable_Resource.adoc @@ -16,36 +16,41 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.configurable.Resource :keywords: helidon, config, io.helidon.common.configurable.Resource :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.configurable.Resource +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.configurable.Resource ----- += Resource (common.configurable) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.Resource[io.helidon.common.configurable.Resource] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description +|`content` |string |{nbsp} |Base64 encoded content of the resource +|`content-plain` |string |{nbsp} |Plain text content of the resource |`path` |string |{nbsp} |File system path to the resource. -|`resource-path` |string |{nbsp} |Classpath location of the resource. |`proxy-host` |string |{nbsp} |Host of the proxy when using url. +|`proxy-port` |int |{nbsp} |Port of the proxy when using url. +|`resource-path` |string |{nbsp} |Classpath location of the resource. |`uri` |URI |{nbsp} |URI of the resource. |`use-proxy` |boolean |`true` |Whether to use proxy. Only used if proxy-host is defined as well. -|`content-plain` |string |{nbsp} |Plain text content of the resource -|`proxy-port` |int |{nbsp} |Port of the proxy when using url. -|`content` |string |{nbsp} |Base64 encoded content of the resource |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.configurable.ScheduledThreadPoolSupplier.adoc b/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc similarity index 78% rename from docs/includes/config/io.helidon.common.configurable.ScheduledThreadPoolSupplier.adoc rename to docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc index 19c83450ace..62aa4415b44 100644 --- a/docs/includes/config/io.helidon.common.configurable.ScheduledThreadPoolSupplier.adoc +++ b/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc @@ -16,32 +16,37 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.configurable.ScheduledThreadPoolSupplier :keywords: helidon, config, io.helidon.common.configurable.ScheduledThreadPoolSupplier :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.configurable.ScheduledThreadPoolSupplier +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.configurable.ScheduledThreadPoolSupplier ----- += ScheduledThreadPoolSupplier (common.configurable) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.ScheduledThreadPoolSupplier[io.helidon.common.configurable.ScheduledThreadPoolSupplier] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. -|`should-prestart` |boolean |`true` |Whether to prestart core threads in this thread pool executor. |`core-pool-size` |int |`16` |Core pool size of the thread pool executor. |`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. +|`should-prestart` |boolean |`true` |Whether to prestart core threads in this thread pool executor. +|`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.configurable.ThreadPoolSupplier.adoc b/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc similarity index 85% rename from docs/includes/config/io.helidon.common.configurable.ThreadPoolSupplier.adoc rename to docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc index 2d392193f48..6830bbd3d1a 100644 --- a/docs/includes/config/io.helidon.common.configurable.ThreadPoolSupplier.adoc +++ b/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc @@ -16,42 +16,47 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.configurable.ThreadPoolSupplier :keywords: helidon, config, io.helidon.common.configurable.ThreadPoolSupplier :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.configurable.ThreadPoolSupplier +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.configurable.ThreadPoolSupplier ----- += ThreadPoolSupplier (common.configurable) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.ThreadPoolSupplier[io.helidon.common.configurable.ThreadPoolSupplier] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`max-pool-size` |int |`50` |Max pool size of the thread pool executor. +|`core-pool-size` |int |`10` |Core pool size of the thread pool executor. +|`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. |`keep-alive-minutes` |int |`3` |Keep alive minutes of the thread pool executor. -|`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. +|`max-pool-size` |int |`50` |Max pool size of the thread pool executor. +|`queue-capacity` |int |`10000` |Queue capacity of the thread pool executor. |`should-prestart` |boolean |`true` |Whether to prestart core threads in this thread pool executor. -|`core-pool-size` |int |`10` |Core pool size of the thread pool executor. +|`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. +|`virtual-enforced` |boolean |`false` |*Experimental* When configured to `true`, virtual thread executor service must be available, otherwise the built + executor would fail to start. |`virtual-threads` |boolean |`false` |*Experimental* When configured to `true`, an unbounded virtual executor service (project Loom) will be used if available. This is an experimental feature. -

+ If enabled and available, all other configuration options of this executor service are ignored! -|`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. -|`virtual-enforced` |boolean |`false` |*Experimental* When configured to `true`, virtual thread executor service must be available, otherwise the built - executor would fail to start. -|`queue-capacity` |int |`10000` |Queue capacity of the thread pool executor. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.pki.KeyConfig.adoc b/docs/config/io_helidon_common_pki_KeyConfig.adoc similarity index 62% rename from docs/includes/config/io.helidon.common.pki.KeyConfig.adoc rename to docs/config/io_helidon_common_pki_KeyConfig.adoc index a63e02386fd..ed3281c5b68 100644 --- a/docs/includes/config/io.helidon.common.pki.KeyConfig.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig.adoc @@ -16,30 +16,35 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.pki.KeyConfig :keywords: helidon, config, io.helidon.common.pki.KeyConfig :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.pki.KeyConfig +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.pki.KeyConfig ----- += KeyConfig (common.pki) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.pki/io.helidon.common.pki.KeyConfig[io.helidon.common.pki.KeyConfig] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`pem` |link:../../shared/config/io.helidon.common.pki.KeyConfig.PemBuilder.adoc[PemBuilder] |{nbsp} |Update this builder with information from a pem builder. -|`keystore` |link:../../shared/config/io.helidon.common.pki.KeyConfig.KeystoreBuilder.adoc[KeystoreBuilder] |{nbsp} |Update this builder with information from a keystore builder. +|`keystore` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc[KeystoreBuilder] |{nbsp} |Update this builder with information from a keystore builder. +|`pem` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc[PemBuilder] |{nbsp} |Update this builder with information from a pem builder. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.pki.KeyConfig.KeystoreBuilder.adoc b/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc similarity index 80% rename from docs/includes/config/io.helidon.common.pki.KeyConfig.KeystoreBuilder.adoc rename to docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc index e195a080da6..f43a92298ab 100644 --- a/docs/includes/config/io.helidon.common.pki.KeyConfig.KeystoreBuilder.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc @@ -16,51 +16,56 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.pki.KeyConfig.KeystoreBuilder :keywords: helidon, config, io.helidon.common.pki.KeyConfig.KeystoreBuilder :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.pki.KeyConfig.KeystoreBuilder +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.pki.KeyConfig.KeystoreBuilder ----- += KeystoreBuilder (common.pki.KeyConfig) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io.helidon.common.pki.KeyConfig.KeystoreBuilder[io.helidon.common.pki.KeyConfig.KeystoreBuilder] + + +== Configuration options + Required configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`resource` |link:../../shared/config/io.helidon.common.configurable.Resource.adoc[Resource] |{nbsp} |Keystore resource definition. +|`resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Keystore resource definition. |=== Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`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 updated builder instance +|`cert-chain.alias` |string |{nbsp} |Alias of an X.509 chain. |`cert.alias` |string |{nbsp} |Alias of X.509 certificate of public key. Used to load both the certificate and public key. -|`cert-chain.alias` |string |{nbsp} |Alias of an X.509 chain. -|`passphrase` |string |{nbsp} |Pass-phrase of the keystore (supported with JKS and PKCS12 keystores). |`key.alias` |string |`1` |Alias of the private key in the keystore. |`key.passphrase` |string |{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. +|`passphrase` |string |{nbsp} |Pass-phrase of the keystore (supported with JKS and PKCS12 keystores). +|`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 updated builder instance |`type` |string |`PKCS12` |Set type of keystore. Defaults to "PKCS12", expected are other keystore types supported by java then can store keys under aliases. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.common.pki.KeyConfig.PemBuilder.adoc b/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc similarity index 65% rename from docs/includes/config/io.helidon.common.pki.KeyConfig.PemBuilder.adoc rename to docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc index 28477f94a66..24a817f54be 100644 --- a/docs/includes/config/io.helidon.common.pki.KeyConfig.PemBuilder.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc @@ -16,32 +16,37 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.common.pki.KeyConfig.PemBuilder :keywords: helidon, config, io.helidon.common.pki.KeyConfig.PemBuilder :basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.pki.KeyConfig.PemBuilder +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.common.pki.KeyConfig.PemBuilder ----- += PemBuilder (common.pki.KeyConfig) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io.helidon.common.pki.KeyConfig.PemBuilder[io.helidon.common.pki.KeyConfig.PemBuilder] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`cert-chain.resource` |link:../../shared/config/io.helidon.common.configurable.Resource.adoc[Resource] |{nbsp} |Load certificate chain from PEM resource. -|`key.resource` |link:../../shared/config/io.helidon.common.configurable.Resource.adoc[Resource] |{nbsp} |Read a private key from PEM format from a resource definition. +|`cert-chain.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Load certificate chain from PEM resource. |`key.passphrase` |string |{nbsp} |Passphrase for private key. If the key is encrypted (and in PEM PKCS#8 format), this passphrase will be used to decrypt it. +|`key.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Read a private key from PEM format from a resource definition. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_Bulkhead.adoc b/docs/config/io_helidon_faulttolerance_Bulkhead.adoc new file mode 100644 index 00000000000..dc2d2095d6e --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_Bulkhead.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.Bulkhead +:keywords: helidon, config, io.helidon.faulttolerance.Bulkhead +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.Bulkhead +include::{rootdir}/includes/attributes.adoc[] + += Bulkhead (faulttolerance) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Bulkhead[io.helidon.faulttolerance.Bulkhead] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cancel-source` |boolean |`true` |Policy to cancel any source stage if the value return by Bulkhead#invoke + is cancelled. Default is `true`; mostly used by FT MP to change default. +|`limit` |int |`10` |Maximal number of parallel requests going through this bulkhead. + When the limit is reached, additional requests are enqueued. +|`name` |string |`Bulkhead-` |A name assigned for debugging, error reporting or configuration purposes. +|`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 io.helidon.faulttolerance.BulkheadException. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc b/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc new file mode 100644 index 00000000000..3bc2278e15a --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.CircuitBreaker +:keywords: helidon, config, io.helidon.faulttolerance.CircuitBreaker +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.CircuitBreaker +include::{rootdir}/includes/attributes.adoc[] + += CircuitBreaker (faulttolerance) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.CircuitBreaker[io.helidon.faulttolerance.CircuitBreaker] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cancel-source` |boolean |`true` |Policy to cancel any source stage if the value return by CircuitBreaker#invoke + is cancelled. Default is `true`; mostly used by FT MP to change default. +|`delay` |Duration |`PT5S` |How long to wait before transitioning from open to half-open state. +|`error-ratio` |int |`60` |How many failures out of 100 will trigger the circuit to open. + This is adapted to the #volume(int) used to handle the window of requests. +If errorRatio is 40, and volume is 10, 4 failed requests will open the circuit. +|`name` |string |`CircuitBreaker-` |A name assigned for debugging, error reporting or configuration purposes. +|`success-threshold` |int |`1` |How many successful calls will close a half-open circuit. + Nevertheless the first failed call will open the circuit again. +|`volume` |int |`10` |Rolling window size used to calculate ratio of failed requests. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_Retry.adoc b/docs/config/io_helidon_faulttolerance_Retry.adoc new file mode 100644 index 00000000000..ec3ad69b600 --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_Retry.adoc @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.Retry +:keywords: helidon, config, io.helidon.faulttolerance.Retry +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.Retry +include::{rootdir}/includes/attributes.adoc[] + += Retry (faulttolerance) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Retry[io.helidon.faulttolerance.Retry] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cancel-source` |boolean |`true` |Policy to cancel any source stage if the value return by Retry#invoke + is cancelled. Default is `true`; mostly used by FT MP to change default. +|`name` |string |`Retry-` |A name assigned for debugging, error reporting or configuration purposes. +|`overall-timeout` |Duration |`PT1S` |Overall timeout. + When overall timeout is reached, execution terminates (even if the retry policy + was not exhausted). +|`retry-policy` |io.helidon.faulttolerance.Retry.RetryPolicy (service provider interface) |{nbsp} |Configure a retry policy to use to calculate delays between retries. + Defaults to a io.helidon.faulttolerance.Retry.JitterRetryPolicy + with 4 calls (initial call + 3 retries), delay of 200 millis and a jitter of 50 millis. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc b/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc new file mode 100644 index 00000000000..706052f8652 --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.Retry.DelayingRetryPolicy +:keywords: helidon, config, io.helidon.faulttolerance.Retry.DelayingRetryPolicy +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.Retry.DelayingRetryPolicy +include::{rootdir}/includes/attributes.adoc[] + += DelayingRetryPolicy (faulttolerance.Retry) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io.helidon.faulttolerance.Retry.DelayingRetryPolicy[io.helidon.faulttolerance.Retry.DelayingRetryPolicy] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`calls` |int |`3` |Total number of calls (first + retries). +|`delay` |Duration |`PT0.2S` |Base delay between the invocations. +|`delay-factor` |double |`2` |A delay multiplication factor. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc b/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc new file mode 100644 index 00000000000..49f566bce16 --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.Retry.JitterRetryPolicy +:keywords: helidon, config, io.helidon.faulttolerance.Retry.JitterRetryPolicy +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.Retry.JitterRetryPolicy +include::{rootdir}/includes/attributes.adoc[] + += JitterRetryPolicy (faulttolerance.Retry) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io.helidon.faulttolerance.Retry.JitterRetryPolicy[io.helidon.faulttolerance.Retry.JitterRetryPolicy] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`calls` |int |`3` |Total number of calls (first + retries). +|`delay` |Duration |`PT0.2S` |Base delay between the invocations. +|`jitter` |Duration |`PT0.05S` |Random part of the delay. + A number between `[-jitter,+jitter]` is applied to delay each time + delay is calculated. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_faulttolerance_Timeout.adoc b/docs/config/io_helidon_faulttolerance_Timeout.adoc new file mode 100644 index 00000000000..594b8df4337 --- /dev/null +++ b/docs/config/io_helidon_faulttolerance_Timeout.adoc @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.faulttolerance.Timeout +:keywords: helidon, config, io.helidon.faulttolerance.Timeout +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.faulttolerance.Timeout +include::{rootdir}/includes/attributes.adoc[] + += Timeout (faulttolerance) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Timeout[io.helidon.faulttolerance.Timeout] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cancel-source` |boolean |`true` |Cancel source if destination stage is cancelled. +|`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. +|`name` |string |`Timeout-` |A name assigned for debugging, error reporting or configuration purposes. +|`timeout` |Duration |`PT10S` |Timeout duration. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.health.HealthSupport.adoc b/docs/config/io_helidon_health_HealthSupport.adoc similarity index 75% rename from docs/includes/config/io.helidon.health.HealthSupport.adoc rename to docs/config/io_helidon_health_HealthSupport.adoc index e827a916de8..d08f1c762d6 100644 --- a/docs/includes/config/io.helidon.health.HealthSupport.adoc +++ b/docs/config/io_helidon_health_HealthSupport.adoc @@ -16,36 +16,46 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.health.HealthSupport :keywords: helidon, config, io.helidon.health.HealthSupport :basic-table-intro: The table below lists the configuration keys that configure io.helidon.health.HealthSupport +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.health.HealthSupport ----- += HealthSupport (health) Configuration +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.health/io.helidon.health.HealthSupport[io.helidon.health.HealthSupport] + + +This is a standalone configuration type, prefix from configuration root: `health` + + + +== Configuration options -==== Configuration options Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`exclude-classes` |Class[] |{nbsp} |A class may be excluded from invoking health checks on it. - This allows configurable approach to disabling broken health-checks. -|`timeout-millis` |long |`10000` |health endpoint timeout (ms) +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`enabled` |boolean |`true` |HealthSupport can be disabled by invoking this method. |`exclude` |string[] |{nbsp} |Add health checks to a black list. Health check results that match by name with a blacklisted records will not be part of the result. +|`exclude-classes` |Class[] |{nbsp} |A class may be excluded from invoking health checks on it. + This allows configurable approach to disabling broken health-checks. |`include` |string[] |{nbsp} |Add health checks to a white list (in case #includeAll is set to `false`. -|`enabled` |boolean |`true` |HealthSupport can be disabled by invoking this method. -|`web-context` |string |{nbsp} |Sets the web context to use for the service's endpoint. |`routing` |string |{nbsp} |Sets the routing name to use for setting up the service's endpoint. -|`cors` |link:../../includes/config/io.helidon.webserver.cors.CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`timeout-millis` |long |`10000` |health endpoint timeout (ms) +|`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/includes/config/io.helidon.media.common.MediaContext.adoc b/docs/config/io_helidon_media_common_MediaContext.adoc similarity index 84% rename from docs/includes/config/io.helidon.media.common.MediaContext.adoc rename to docs/config/io_helidon_media_common_MediaContext.adoc index 3f25d214fd7..900078362ce 100644 --- a/docs/includes/config/io.helidon.media.common.MediaContext.adoc +++ b/docs/config/io_helidon_media_common_MediaContext.adoc @@ -16,25 +16,26 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.media.common.MediaContext :keywords: helidon, config, io.helidon.media.common.MediaContext :basic-table-intro: The table below lists the configuration keys that configure io.helidon.media.common.MediaContext +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.media.common.MediaContext ----- += io.helidon.media.common.MediaContext +// tag::config[] +link:{javadoc-base-url}/io.helidon.media.common/io.helidon.media.common.MediaContext[io.helidon.media.common.MediaContext] -==== Configuration options +== Configuration options + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description @@ -46,3 +47,5 @@ Optional configuration options: |`services` |Object[] |{nbsp} |Configuration section for each service. Each entry has to have "name" parameter. It is also used for filtering of loaded services |=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc new file mode 100644 index 00000000000..276a1958171 --- /dev/null +++ b/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.BaseMetricsSettings +:keywords: helidon, config, io.helidon.metrics.api.BaseMetricsSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.BaseMetricsSettings +include::{rootdir}/includes/attributes.adoc[] + += BaseMetricsSettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.BaseMetricsSettings[io.helidon.metrics.api.BaseMetricsSettings] + + +[source,text] +.Config key +---- +metrics.base +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`enabled` |boolean |`true` |Sets whether base metrics should be enabled. +|`x.y.enabled` |boolean |`true` |Sets whether a specific base metric should be enabled. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc new file mode 100644 index 00000000000..65dc3cc17dd --- /dev/null +++ b/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.ComponentMetricsSettings +:keywords: helidon, config, io.helidon.metrics.api.ComponentMetricsSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.ComponentMetricsSettings +include::{rootdir}/includes/attributes.adoc[] + += ComponentMetricsSettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.ComponentMetricsSettings[io.helidon.metrics.api.ComponentMetricsSettings] + + +[source,text] +.Config key +---- +metrics +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,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/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc new file mode 100644 index 00000000000..ed508fa76b1 --- /dev/null +++ b/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings +:keywords: helidon, config, io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings +include::{rootdir}/includes/attributes.adoc[] + += KeyPerformanceIndicatorMetricsSettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings[io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings] + + +[source,text] +.Config key +---- +metrics.key-performance-indicators +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`extended` |boolean |{nbsp} |Sets whether exntended KPI metrics should be enabled in the settings. +|`long-running-requests.threshold-ms` |long |`10000` |Sets the long-running request threshold (in ms). + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_metrics_api_MetricsSettings.adoc b/docs/config/io_helidon_metrics_api_MetricsSettings.adoc new file mode 100644 index 00000000000..9b750232f28 --- /dev/null +++ b/docs/config/io_helidon_metrics_api_MetricsSettings.adoc @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.MetricsSettings +:keywords: helidon, config, io.helidon.metrics.api.MetricsSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.MetricsSettings +include::{rootdir}/includes/attributes.adoc[] + += MetricsSettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.MetricsSettings[io.helidon.metrics.api.MetricsSettings] + + +[source,text] +.Config key +---- +metrics +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`appName` |string |{nbsp} |Sets the value for the `_app` tag to be applied to all metrics. +|`base` |xref:{rootdir}/config/io_helidon_metrics_api_BaseMetricsSettings.adoc[BaseMetricsSettings] |{nbsp} |Set the base metrics settings. +|`enabled` |boolean |{nbsp} |Sets whether metrics should be enabled. +|`key-performance-indicators` |xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings] |{nbsp} |Set the KPI metrics settings. +|`registries` |xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[RegistrySettings[]] |{nbsp} |Sets the registry settings for the specified registry type. +|`tags` |Map<string, string> |{nbsp} |Sets the global tags to be applied to all metrics. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc b/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc new file mode 100644 index 00000000000..ab928b9bcee --- /dev/null +++ b/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.RegistryFilterSettings +:keywords: helidon, config, io.helidon.metrics.api.RegistryFilterSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.RegistryFilterSettings +include::{rootdir}/includes/attributes.adoc[] + += RegistryFilterSettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.RegistryFilterSettings[io.helidon.metrics.api.RegistryFilterSettings] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`exclude` |string |{nbsp} |Regular expression matching metric names to exclude +|`include` |string |{nbsp} |Regular expression matching metrics names to include + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_metrics_api_RegistrySettings.adoc b/docs/config/io_helidon_metrics_api_RegistrySettings.adoc new file mode 100644 index 00000000000..b54459fce1f --- /dev/null +++ b/docs/config/io_helidon_metrics_api_RegistrySettings.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.RegistrySettings +:keywords: helidon, config, io.helidon.metrics.api.RegistrySettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.RegistrySettings +include::{rootdir}/includes/attributes.adoc[] + += RegistrySettings (metrics.api) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.RegistrySettings[io.helidon.metrics.api.RegistrySettings] + + +[source,text] +.Config key +---- +metrics.<metric-type> +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`enabled` |boolean |`true` |Sets whether the metric type should be enabled. +|`filter` |xref:{rootdir}/config/io_helidon_metrics_api_RegistryFilterSettings.adoc[RegistryFilterSettings] |{nbsp} |Name filtering, featuring optional exclude and include settings + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc new file mode 100644 index 00000000000..f37f56240ac --- /dev/null +++ b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.microprofile.openapi.MPOpenAPISupport +:keywords: helidon, config, io.helidon.microprofile.openapi.MPOpenAPISupport +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.microprofile.openapi.MPOpenAPISupport +include::{rootdir}/includes/attributes.adoc[] + += MPOpenAPISupport (microprofile.openapi) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io.helidon.microprofile.openapi.MPOpenAPISupport[io.helidon.microprofile.openapi.MPOpenAPISupport] + + +[source,text] +.Config key +---- +mp.openapi +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. +|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. +|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. +|`scan.classes` |string[] |{nbsp} |Specify the list of classes to scan. +|`scan.disable` |boolean |`false` |Disable annotation scanning. +|`scan.exclude.classes` |string[] |{nbsp} |Specify the list of classes to exclude from scans. +|`scan.exclude.packages` |string[] |{nbsp} |Specify the list of packages to exclude from scans. +|`scan.packages` |string[] |{nbsp} |Specify the list of packages to scan. +|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. +|`servers` |string[] |{nbsp} |Sets servers. +|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. +|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. +|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_openapi_OpenAPISupport.adoc b/docs/config/io_helidon_openapi_OpenAPISupport.adoc new file mode 100644 index 00000000000..dcce6c86ece --- /dev/null +++ b/docs/config/io_helidon_openapi_OpenAPISupport.adoc @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.openapi.OpenAPISupport +:keywords: helidon, config, io.helidon.openapi.OpenAPISupport +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.openapi.OpenAPISupport +include::{rootdir}/includes/attributes.adoc[] + += OpenAPISupport (openapi) Configuration + +// tag::config[] + +OpenAPI support configuration + + +Type: link:{javadoc-base-url}/io.helidon.openapi/io.helidon.openapi.OpenAPISupport[io.helidon.openapi.OpenAPISupport] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. +|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.openapi.SEOpenAPISupport.adoc b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc similarity index 81% rename from docs/includes/config/io.helidon.openapi.SEOpenAPISupport.adoc rename to docs/config/io_helidon_openapi_SEOpenAPISupport.adoc index c4715f9e272..58435c54b52 100644 --- a/docs/includes/config/io.helidon.openapi.SEOpenAPISupport.adoc +++ b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc @@ -16,39 +16,50 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.openapi.SEOpenAPISupport :keywords: helidon, config, io.helidon.openapi.SEOpenAPISupport :basic-table-intro: The table below lists the configuration keys that configure io.helidon.openapi.SEOpenAPISupport +include::{rootdir}/includes/attributes.adoc[] + += SEOpenAPISupport (openapi) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.openapi/io.helidon.openapi.SEOpenAPISupport[io.helidon.openapi.SEOpenAPISupport] + [source,text] -.Type +.Config key ---- -io.helidon.openapi.SEOpenAPISupport +openapi ---- -==== Configuration options - +== Configuration options Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. -|`cors` |link:../../shared/config/io.helidon.webserver.cors.CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. -|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. -|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. -|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. -|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. |`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. +|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. +|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. |`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. +|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. |`servers` |string[] |{nbsp} |Sets servers. -|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. +|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. +|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_Security.adoc b/docs/config/io_helidon_security_Security.adoc new file mode 100644 index 00000000000..bdbec475e47 --- /dev/null +++ b/docs/config/io_helidon_security_Security.adoc @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.Security +:keywords: helidon, config, io.helidon.security.Security +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.Security +include::{rootdir}/includes/attributes.adoc[] + += Security (security) Configuration + +// tag::config[] + +Configuration of security providers, integration and other security options + + +Type: link:{javadoc-base-url}/io.helidon.security/io.helidon.security.Security[io.helidon.security.Security] + + +This is a standalone configuration type, prefix from configuration root: `security` + + + +== Configuration options + +Required configuration options: +[cols="3,3,2,5a"] +|=== +|key |type |default value |description + +|`providers` |io.helidon.security.spi.SecurityProvider[] (service provider interface) |{nbsp} |Add a provider, works as #addProvider(io.helidon.security.spi.SecurityProvider, String), where the name is set + to `Class#getSimpleName()`. + +|=== + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`default-authentication-provider` |string (service provider interface) |{nbsp} |ID of the default authentication provider +|`default-authorization-provider` |string |{nbsp} |ID of the default authorization provider +|`enabled` |boolean |`true` |Security can be disabled using configuration, or explicitly. + By default, security instance is enabled. + Disabled security instance will not perform any checks and allow + all requests. +|`environment.executor-service` |xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc[ThreadPoolSupplier] |{nbsp} |Configure executor service to be used for blocking operations within security. +|`environment.server-time` |xref:{rootdir}/config/io_helidon_security_SecurityTime.adoc[SecurityTime] |{nbsp} |Server time to use when evaluating security policies that depend on time. +|`provider-policy.class-name` |Class |{nbsp} |Provider selection policy class name, only used when type is set to CLASS +|`provider-policy.type` |ProviderSelectionPolicyType (FIRST, COMPOSITE, CLASS) |`FIRST` |Type of the policy. +|`secrets` |Map<string, string> (documented for specific cases) |{nbsp} |Configured secrets +|`secrets.*.config` |io.helidon.security.SecretsProviderConfig (service provider interface) |{nbsp} |Configuration specific to the secret provider +|`secrets.*.name` |string |{nbsp} |Name of the secret, used for lookup +|`secrets.*.provider` |string |{nbsp} |Name of the secret provider +|`tracing.enabled` |boolean |`true` |Whether or not tracing should be enabled. If set to false, security tracer will be a no-op tracer. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_SecurityTime.adoc b/docs/config/io_helidon_security_SecurityTime.adoc new file mode 100644 index 00000000000..e50f3afed00 --- /dev/null +++ b/docs/config/io_helidon_security_SecurityTime.adoc @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.SecurityTime +:keywords: helidon, config, io.helidon.security.SecurityTime +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.SecurityTime +include::{rootdir}/includes/attributes.adoc[] + += SecurityTime (security) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security/io.helidon.security.SecurityTime[io.helidon.security.SecurityTime] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|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). +|`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). + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc b/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc new file mode 100644 index 00000000000..a4a01b863bf --- /dev/null +++ b/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.abac.AbacProvider +:keywords: helidon, config, io.helidon.security.providers.abac.AbacProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.abac.AbacProvider +include::{rootdir}/includes/attributes.adoc[] + += AbacProvider (security.providers.abac) Configuration + +// tag::config[] + +Attribute Based Access Control provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.abac/io.helidon.security.providers.abac.AbacProvider[io.helidon.security.providers.abac.AbacProvider] + + +[source,text] +.Config key +---- +abac +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthorizationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`fail-if-none-validated` |boolean |`true` |Whether to fail if NONE of the attributes is validated. +|`fail-on-unvalidated` |boolean |`true` |Whether to fail if any attribute is left unvalidated. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc b/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc new file mode 100644 index 00000000000..9afe67df243 --- /dev/null +++ b/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.common.OutboundConfig +:keywords: helidon, config, io.helidon.security.providers.common.OutboundConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.common.OutboundConfig +include::{rootdir}/includes/attributes.adoc[] + += OutboundConfig (security.providers.common) Configuration + +// tag::config[] + +Outbound configuration for outbound security + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io.helidon.security.providers.common.OutboundConfig[io.helidon.security.providers.common.OutboundConfig] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`outbound` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget[]] |{nbsp} |Add a new target configuration. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc b/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc new file mode 100644 index 00000000000..e9b84f9e8fd --- /dev/null +++ b/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.common.OutboundTarget +:keywords: helidon, config, io.helidon.security.providers.common.OutboundTarget +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.common.OutboundTarget +include::{rootdir}/includes/attributes.adoc[] + += OutboundTarget (security.providers.common) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io.helidon.security.providers.common.OutboundTarget[io.helidon.security.providers.common.OutboundTarget] + + + + +== Configuration options + +Required configuration options: +[cols="3,3,2,5a"] +|=== +|key |type |default value |description + +|`name` |string |{nbsp} |Configure the name of this outbound target. + +|=== + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`hosts` |string[] |{nbsp} |Add supported host for this target. May be called more than once to add more hosts. + + Valid examples: + +- localhost + +- www.google.com + +- 127.0.0.1 + +- *.oracle.com + +- 192.169.*.* + +- *.google.* + + +|`methods` |string[] |{nbsp} |Add supported method for this target. May be called more than once to add more methods. + The method is tested as is ignoring case against the used method. +|`paths` |string[] |{nbsp} |Add supported paths for this target. May be called more than once to add more paths. + The path is tested as is against called path, and also tested as a regular expression. +|`transport` |string[] |{nbsp} |Add supported transports for this target. May be called more than once to add more transports. + + Valid examples: + +- http + +- https + +There is no wildcard support + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc b/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc new file mode 100644 index 00000000000..3993b19f82c --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser +:keywords: helidon, config, io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser +include::{rootdir}/includes/attributes.adoc[] + += ConfigUser (security.providers.httpauth.ConfigUserStore) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth.ConfigUserStore/io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser[io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`login` |string |{nbsp} |User's login +|`password` |string |{nbsp} |User's password +|`roles` |string[] |{nbsp} |List of roles the user is in + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc new file mode 100644 index 00000000000..2fd7ecb8bed --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpauth.HttpBasicAuthProvider +:keywords: helidon, config, io.helidon.security.providers.httpauth.HttpBasicAuthProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpauth.HttpBasicAuthProvider +include::{rootdir}/includes/attributes.adoc[] + += HttpBasicAuthProvider (security.providers.httpauth) Configuration + +// tag::config[] + +HTTP Basic Authentication provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth/io.helidon.security.providers.httpauth.HttpBasicAuthProvider[io.helidon.security.providers.httpauth.HttpBasicAuthProvider] + + +[source,text] +.Config key +---- +http-basic-auth +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`optional` |boolean |`false` |Whether authentication is required. + By default, request will fail if the authentication cannot be verified. + If set to false, request will process and this provider will abstain. +|`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). + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc new file mode 100644 index 00000000000..8892884a022 --- /dev/null +++ b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.oidc.OidcProvider +:keywords: helidon, config, io.helidon.security.providers.oidc.OidcProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.oidc.OidcProvider +include::{rootdir}/includes/attributes.adoc[] + += OidcProvider (security.providers.oidc) Configuration + +// tag::config[] + +Open ID Connect security provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc/io.helidon.security.providers.oidc.OidcProvider[io.helidon.security.providers.oidc.OidcProvider] + + +[source,text] +.Config key +---- +oidc +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.AuthenticationProvider` +- `io.helidon.security.spi.SecurityProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`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. +|`base-scopes` |string |`openid` |Configure base scopes. + By default this is `DEFAULT_BASE_SCOPES`. + If scope has a qualifier, it must be used here. +|`client-id` |string |{nbsp} |Client ID as generated by OIDC server. +|`client-secret` |string |{nbsp} |Client secret as generated by OIDC server. + Used to authenticate this application with the server when requesting + JWT based on a code. +|`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client. +|`cookie-domain` |string |{nbsp} |Domain the cookie is valid for. + Not used by default. +|`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`. +|`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`. +|`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 + "Strict" or "Lax". +|`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`. +|`force-https-redirects` |boolean |`false` |Force HTTPS for redirects to identity provider. + Defaults to `false`. +|`frontend-uri` |string |{nbsp} |Full URI of this application that is visible from user browser. + Used to redirect request back from identity server after successful login. +|`header-token` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |A TokenHandler to + process header containing a JWT. + Default is "Authorization" header with a prefix "bearer ". +|`header-use` |boolean |`true` |Whether to expect JWT in a header field. +|`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). +|`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` +|`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. + token-endpoint-uri). +|`oidc-metadata.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Resource configuration for OIDC Metadata + containing endpoints to various identity services, as well as information about the identity server. +|`optional` |boolean |`false` |Whether authentication is required. + By default, request will fail if the authentication cannot be verified. + If set to true, request will process and this provider will abstain. +|`outbound` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget[]] |{nbsp} |Add a new target configuration. +|`propagate` |boolean |`false` |Whether to propagate identity. +|`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` +|`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used. + Defaults to `DEFAULT_PROXY_PROTOCOL`. +|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used. +|`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. + This behavior can be overridden by setting redirect to false. When token is not present in the request, the client + 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` +|`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` +|`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. +|`server-type` |string |`@default` |Configure one of the supported types of identity servers. + + If the type does not have an explicit mapping, a warning is logged and the default implementation is used. +|`sign-jwk.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |A resource pointing to JWK with public keys of signing certificates used + to validate JWT. +|`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 + + +|`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. +|`use-jwt-groups` |boolean |`true` |Claim `groups` from JWT will be used to automatically add + groups to current subject (may be used with jakarta.annotation.security.RolesAllowed annotation). +|`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. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc new file mode 100644 index 00000000000..ab632102fe6 --- /dev/null +++ b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.oidc.common.OidcConfig +:keywords: helidon, config, io.helidon.security.providers.oidc.common.OidcConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.oidc.common.OidcConfig +include::{rootdir}/includes/attributes.adoc[] + += OidcConfig (security.providers.oidc.common) Configuration + +// tag::config[] + +Open ID Connect configuration + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io.helidon.security.providers.oidc.common.OidcConfig[io.helidon.security.providers.oidc.common.OidcConfig] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`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. +|`base-scopes` |string |`openid` |Configure base scopes. + By default this is `DEFAULT_BASE_SCOPES`. + If scope has a qualifier, it must be used here. +|`client-id` |string |{nbsp} |Client ID as generated by OIDC server. +|`client-secret` |string |{nbsp} |Client secret as generated by OIDC server. + Used to authenticate this application with the server when requesting + JWT based on a code. +|`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client. +|`cookie-domain` |string |{nbsp} |Domain the cookie is valid for. + Not used by default. +|`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`. +|`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`. +|`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 + "Strict" or "Lax". +|`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`. +|`force-https-redirects` |boolean |`false` |Force HTTPS for redirects to identity provider. + Defaults to `false`. +|`frontend-uri` |string |{nbsp} |Full URI of this application that is visible from user browser. + Used to redirect request back from identity server after successful login. +|`header-token` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |A TokenHandler to + process header containing a JWT. + Default is "Authorization" header with a prefix "bearer ". +|`header-use` |boolean |`true` |Whether to expect JWT in a header field. +|`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). +|`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` +|`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. + token-endpoint-uri). +|`oidc-metadata.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Resource configuration for OIDC Metadata + containing endpoints to various identity services, as well as information about the identity server. +|`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` +|`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used. + Defaults to `DEFAULT_PROXY_PROTOCOL`. +|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used. +|`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. + This behavior can be overridden by setting redirect to false. When token is not present in the request, the client + 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` +|`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` +|`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. +|`server-type` |string |`@default` |Configure one of the supported types of identity servers. + + If the type does not have an explicit mapping, a warning is logged and the default implementation is used. +|`sign-jwk.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |A resource pointing to JWK with public keys of signing certificates used + to validate JWT. +|`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 + + +|`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. +|`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. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_util_TokenHandler.adoc b/docs/config/io_helidon_security_util_TokenHandler.adoc new file mode 100644 index 00000000000..f6c69c39edc --- /dev/null +++ b/docs/config/io_helidon_security_util_TokenHandler.adoc @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.util.TokenHandler +:keywords: helidon, config, io.helidon.security.util.TokenHandler +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.util.TokenHandler +include::{rootdir}/includes/attributes.adoc[] + += TokenHandler (security.util) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.util/io.helidon.security.util.TokenHandler[io.helidon.security.util.TokenHandler] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`format` |string |{nbsp} |Token format for creating outbound tokens. +|`header` |string |{nbsp} |Set the name of header to look into to extract the token. +|`prefix` |string |{nbsp} |Set the prefix of header value to extract the token. +|`regexp` |string |{nbsp} |Set the token pattern (Regular expression) to extract the token. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc b/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc new file mode 100644 index 00000000000..8e2c810d283 --- /dev/null +++ b/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.servicecommon.rest.HelidonRestServiceSupport +:keywords: helidon, config, io.helidon.servicecommon.rest.HelidonRestServiceSupport +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.servicecommon.rest.HelidonRestServiceSupport +include::{rootdir}/includes/attributes.adoc[] + += HelidonRestServiceSupport (servicecommon.rest) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io.helidon.servicecommon.rest.HelidonRestServiceSupport[io.helidon.servicecommon.rest.HelidonRestServiceSupport] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`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/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc b/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc new file mode 100644 index 00000000000..a30a4aa2f14 --- /dev/null +++ b/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.servicecommon.rest.RestServiceSettings +:keywords: helidon, config, io.helidon.servicecommon.rest.RestServiceSettings +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.servicecommon.rest.RestServiceSettings +include::{rootdir}/includes/attributes.adoc[] + += RestServiceSettings (servicecommon.rest) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io.helidon.servicecommon.rest.RestServiceSettings[io.helidon.servicecommon.rest.RestServiceSettings] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`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/config/io_helidon_tracing_Tracer.adoc b/docs/config/io_helidon_tracing_Tracer.adoc new file mode 100644 index 00000000000..404836eda57 --- /dev/null +++ b/docs/config/io_helidon_tracing_Tracer.adoc @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.tracing.Tracer +:keywords: helidon, config, io.helidon.tracing.Tracer +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.tracing.Tracer +include::{rootdir}/includes/attributes.adoc[] + += Tracer (tracing) Configuration + +// tag::config[] + +Jaeger tracer configuration. + + +Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.Tracer[io.helidon.tracing.Tracer] + + +This is a standalone configuration type, prefix from configuration root: `tracing` + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should + use a no-op tracer. +|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. +|`host` |string |{nbsp} |Host to use to connect to tracing collector. + Default is defined by each tracing integration. +|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. +|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. + Default is defined by each tracing integration. +|`port` |int |{nbsp} |Port to use to connect to tracing collector. + Default is defined by each tracing integration. +|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. + Default is defined by each tracing integration. +|`service` |string |{nbsp} |Service name of the traced service. +|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_tracing_TracerBuilder.adoc b/docs/config/io_helidon_tracing_TracerBuilder.adoc new file mode 100644 index 00000000000..9b8e6cae1a4 --- /dev/null +++ b/docs/config/io_helidon_tracing_TracerBuilder.adoc @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.tracing.TracerBuilder +:keywords: helidon, config, io.helidon.tracing.TracerBuilder +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.tracing.TracerBuilder +include::{rootdir}/includes/attributes.adoc[] + += TracerBuilder (tracing) Configuration + +// tag::config[] + +OpenTracing tracer configuration. + + +Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.TracerBuilder[io.helidon.tracing.TracerBuilder] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should + use a no-op tracer. +|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. +|`host` |string |{nbsp} |Host to use to connect to tracing collector. + Default is defined by each tracing integration. +|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. +|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. + Default is defined by each tracing integration. +|`port` |int |{nbsp} |Port to use to connect to tracing collector. + Default is defined by each tracing integration. +|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. + Default is defined by each tracing integration. +|`service` |string |{nbsp} |Service name of the traced service. +|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.webserver.SocketConfiguration.adoc b/docs/config/io_helidon_webserver_SocketConfiguration.adoc similarity index 81% rename from docs/includes/config/io.helidon.webserver.SocketConfiguration.adoc rename to docs/config/io_helidon_webserver_SocketConfiguration.adoc index 3592215d1a5..bbdada32f96 100644 --- a/docs/includes/config/io.helidon.webserver.SocketConfiguration.adoc +++ b/docs/config/io_helidon_webserver_SocketConfiguration.adoc @@ -16,23 +16,26 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.webserver.SocketConfiguration :keywords: helidon, config, io.helidon.webserver.SocketConfiguration :basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.SocketConfiguration +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.webserver.SocketConfiguration ----- += SocketConfiguration (webserver) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.SocketConfiguration[io.helidon.webserver.SocketConfiguration] + + +== Configuration options + Required configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description @@ -43,44 +46,49 @@ Required configuration options: Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description |`backlog` |int |`1024` |Configures a maximum length of the queue of incoming connections on the server socket. -

+ Default value is #DEFAULT_BACKLOG_SIZE. -|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. -

- Default is `4096` -|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is - `0` then any available ephemeral port will be used. -|`timeout-millis` |long |`0` |Socket timeout in milliseconds -|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the - server socket. -

- If `0` then use implementation default. |[.line-through]#`bind-address`# |string |{nbsp} |*Deprecated* Configures local address where the server listens on with the server socket. If not configured, then listens an all local addresses. +|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can + request compression using the "Accept-Encoding" header. + + Default is `false` |`host` |string |{nbsp} |A helper method that just calls #bindAddress(String). |`max-header-size` |int |`8192` |Maximal number of bytes of all header values combined. When a bigger value is received, a io.helidon.common.http.Http.Status#BAD_REQUEST_400 is returned. -

+ Default is `8192` -|`tls` |link:../../shared/config/io.helidon.webserver.WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL +|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. + + Default is `4096` +|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS + attacks. +|`max-upgrade-content-length` |int |`65536` |Set a maximum length of the content of an upgrade request. + + Default is `64*1024` +|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is + `0` then any available ephemeral port will be used. +|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the + server socket. + + If `0` then use implementation default. +|`timeout-millis` |long |`0` |Socket timeout in milliseconds +|`tls` |xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL configuration. If this method is called, any other method except for #tls(java.util.function.Supplier)¨ and repeated invocation of this method would be ignored. -

+ If this method is called again, the previous configuration would be ignored. -|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can - request compression using the "Accept-Encoding" header. -

- Default is `false` -|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS - attacks. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder.adoc b/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc similarity index 79% rename from docs/includes/config/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder.adoc rename to docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc index bbd86e4a0cb..9fcb486b0ad 100644 --- a/docs/includes/config/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder.adoc +++ b/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc @@ -16,62 +16,70 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder :keywords: helidon, config, io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder :basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder ----- += SocketConfigurationBuilder (webserver.SocketConfiguration) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.webserver.SocketConfiguration/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder[io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description |`backlog` |int |`1024` |Configures a maximum length of the queue of incoming connections on the server socket. -

+ Default value is #DEFAULT_BACKLOG_SIZE. -|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. -

- Default is `4096` -|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is - `0` then any available ephemeral port will be used. -|`timeout-millis` |long |`0` |Socket timeout in milliseconds -|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the - server socket. -

- If `0` then use implementation default. |[.line-through]#`bind-address`# |string |{nbsp} |*Deprecated* Configures local address where the server listens on with the server socket. If not configured, then listens an all local addresses. +|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can + request compression using the "Accept-Encoding" header. + + Default is `false` |`host` |string |{nbsp} |A helper method that just calls #bindAddress(String). |`max-header-size` |int |`8192` |Maximal number of bytes of all header values combined. When a bigger value is received, a io.helidon.common.http.Http.Status#BAD_REQUEST_400 is returned. -

+ Default is `8192` -|`tls` |link:../../shared/config/io.helidon.webserver.WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL +|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. + + Default is `4096` +|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS + attacks. +|`max-upgrade-content-length` |int |`65536` |Set a maximum length of the content of an upgrade request. + + Default is `64*1024` +|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is + `0` then any available ephemeral port will be used. +|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the + server socket. + + If `0` then use implementation default. +|`timeout-millis` |long |`0` |Socket timeout in milliseconds +|`tls` |xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL configuration. If this method is called, any other method except for #tls(java.util.function.Supplier)¨ and repeated invocation of this method would be ignored. -

+ If this method is called again, the previous configuration would be ignored. -|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can - request compression using the "Accept-Encoding" header. -

- Default is `false` -|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS - attacks. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.webserver.WebServer.adoc b/docs/config/io_helidon_webserver_WebServer.adoc similarity index 80% rename from docs/includes/config/io.helidon.webserver.WebServer.adoc rename to docs/config/io_helidon_webserver_WebServer.adoc index b18d099e6a3..276c49b17b6 100644 --- a/docs/includes/config/io.helidon.webserver.WebServer.adoc +++ b/docs/config/io_helidon_webserver_WebServer.adoc @@ -16,76 +16,84 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.webserver.WebServer :keywords: helidon, config, io.helidon.webserver.WebServer :basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.WebServer +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.webserver.WebServer ----- += WebServer (webserver) Configuration + +// tag::config[] Configuration of the HTTP server. +Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.WebServer[io.helidon.webserver.WebServer] + + This is a standalone configuration type, prefix from configuration root: `server` -==== Configuration options +== Configuration options Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`sockets` |link:../../shared/config/io.helidon.webserver.SocketConfiguration.adoc[SocketConfiguration[]] |{nbsp} |Adds an additional named server socket configuration. As a result, the server will listen - on multiple ports. -

- An additional named server socket may have a dedicated Routing configured - through io.helidon.webserver.WebServer.Builder#addNamedRouting(String, Routing). -|`worker-count` |int |{nbsp} |Sets a count of threads in pool used to process HTTP requests. - Default value is `CPU_COUNT * 2`. -

- Configuration key: `workers` -|`features.print-details` |boolean |`false` |Set to `true` to print detailed feature information on startup. |`backlog` |int |`1024` |Configures a maximum length of the queue of incoming connections on the server socket. -

+ Default value is #DEFAULT_BACKLOG_SIZE. -|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. -

- Default is `4096` -|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is - `0` then any available ephemeral port will be used. -|`timeout-millis` |long |`0` |Socket timeout in milliseconds -|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the - server socket. -

- If `0` then use implementation default. |[.line-through]#`bind-address`# |string |{nbsp} |*Deprecated* Configures local address where the server listens on with the server socket. If not configured, then listens an all local addresses. +|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can + request compression using the "Accept-Encoding" header. + + Default is `false` +|`features.print-details` |boolean |`false` |Set to `true` to print detailed feature information on startup. |`host` |string |{nbsp} |A helper method that just calls #bindAddress(String). |`max-header-size` |int |`8192` |Maximal number of bytes of all header values combined. When a bigger value is received, a io.helidon.common.http.Http.Status#BAD_REQUEST_400 is returned. -

+ Default is `8192` -|`tls` |link:../../shared/config/io.helidon.webserver.WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL +|`max-initial-line-length` |int |`4096` |Maximal number of characters in the initial HTTP line. + + Default is `4096` +|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS + attacks. +|`max-upgrade-content-length` |int |`65536` |Set a maximum length of the content of an upgrade request. + + Default is `64*1024` +|`port` |int |`0` |Configures a server port to listen on with the server socket. If port is + `0` then any available ephemeral port will be used. +|`receive-buffer-size` |int |{nbsp} |Configures proposed value of the TCP receive window that is advertised to the remote peer on the + server socket. + + If `0` then use implementation default. +|`sockets` |xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration.adoc[SocketConfiguration[]] |{nbsp} |Adds an additional named server socket configuration. As a result, the server will listen + on multiple ports. + + An additional named server socket may have a dedicated Routing configured + through io.helidon.webserver.WebServer.Builder#addNamedRouting(String, Routing). +|`timeout-millis` |long |`0` |Socket timeout in milliseconds +|`tls` |xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls] |{nbsp} |Configures SSL for this socket. When configured, the server enforces SSL configuration. If this method is called, any other method except for #tls(java.util.function.Supplier)¨ and repeated invocation of this method would be ignored. -

+ If this method is called again, the previous configuration would be ignored. -|`enable-compression` |boolean |`false` |Enable negotiation for gzip/deflate content encodings. Clients can - request compression using the "Accept-Encoding" header. -

- Default is `false` -|`max-payload-size` |long |{nbsp} |Set a maximum payload size for a client request. Can prevent DoS - attacks. +|`worker-count` |int |{nbsp} |Sets a count of threads in pool used to process HTTP requests. + Default value is `CPU_COUNT * 2`. + + Configuration key: `workers` |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.webserver.WebServerTls.adoc b/docs/config/io_helidon_webserver_WebServerTls.adoc similarity index 74% rename from docs/includes/config/io.helidon.webserver.WebServerTls.adoc rename to docs/config/io_helidon_webserver_WebServerTls.adoc index 14a9086a997..45ddadd00e1 100644 --- a/docs/includes/config/io.helidon.webserver.WebServerTls.adoc +++ b/docs/config/io_helidon_webserver_WebServerTls.adoc @@ -16,46 +16,51 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.webserver.WebServerTls :keywords: helidon, config, io.helidon.webserver.WebServerTls :basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.WebServerTls +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.webserver.WebServerTls ----- += WebServerTls (webserver) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.WebServerTls[io.helidon.webserver.WebServerTls] + + +== Configuration options + Required configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`private-key` |link:../../shared/config/io.helidon.common.pki.KeyConfig.adoc[KeyConfig] |{nbsp} |Configure private key to use for SSL context. +|`private-key` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig] |{nbsp} |Configure private key to use for SSL context. |=== Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`session-timeout-seconds` |long |{nbsp} |Set the timeout for the cached SSL session objects, in seconds. `0` to use the - default value. |`cipher-suite` |string[] |{nbsp} |Set allowed cipher suite. If an empty collection is set, an exception is thrown since it is required to support at least some ciphers. |`client-auth` |ClientAuthentication (REQUIRE, OPTIONAL, NONE) |`none` |Configures whether client authentication will be required or not. +|`enabled` |boolean |`true` |Can be used to disable TLS even if keys are configured. |`session-cache-size` |long |{nbsp} |Set the size of the cache used for storing SSL session objects. `0` to use the default value. -|`trust` |link:../../shared/config/io.helidon.common.pki.KeyConfig.adoc[KeyConfig] |{nbsp} |Set the trust key configuration to be used to validate certificates. -|`enabled` |boolean |`true` |Can be used to disable TLS even if keys are configured. +|`session-timeout-seconds` |long |{nbsp} |Set the timeout for the cached SSL session objects, in seconds. `0` to use the + default value. +|`trust` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig] |{nbsp} |Set the trust key configuration to be used to validate certificates. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/config/io.helidon.webserver.cors.CrossOriginConfig.adoc b/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc similarity index 82% rename from docs/includes/config/io.helidon.webserver.cors.CrossOriginConfig.adoc rename to docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc index e70c1951c6e..24a022742a9 100644 --- a/docs/includes/config/io.helidon.webserver.cors.CrossOriginConfig.adoc +++ b/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc @@ -16,36 +16,41 @@ /////////////////////////////////////////////////////////////////////////////// +ifndef::rootdir[:rootdir: {docdir}/..] :description: Configuration of io.helidon.webserver.cors.CrossOriginConfig :keywords: helidon, config, io.helidon.webserver.cors.CrossOriginConfig :basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.cors.CrossOriginConfig +include::{rootdir}/includes/attributes.adoc[] -[source,text] -.Type ----- -io.helidon.webserver.cors.CrossOriginConfig ----- += CrossOriginConfig (webserver.cors) Configuration +// tag::config[] -==== Configuration options +Type: link:{javadoc-base-url}/io.helidon.webserver.cors/io.helidon.webserver.cors.CrossOriginConfig[io.helidon.webserver.cors.CrossOriginConfig] +== Configuration options + + + Optional configuration options: -[cols="3,3,2,5"] +[cols="3,3,2,5a"] |=== |key |type |default value |description -|`path-pattern` |string |`{+}` |Updates the path prefix for this cross-origin config. -|`allow-headers` |string[] |`*` |Sets the allow headers. -|`max-age-seconds` |long |`3600` |Sets the maximum age. |`allow-credentials` |boolean |`false` |Sets the allow credentials flag. -|`allow-origins` |string[] |`*` |Sets the allowOrigins. -|`expose-headers` |string[] |{nbsp} |Sets the expose headers. +|`allow-headers` |string[] |`*` |Sets the allow headers. |`allow-methods` |string[] |`*` |Sets the allow methods. +|`allow-origins` |string[] |`*` |Sets the allowOrigins. |`enabled` |boolean |`true` |Sets whether this config should be enabled or not. +|`expose-headers` |string[] |{nbsp} |Sets the expose headers. +|`max-age-seconds` |long |`3600` |Sets the maximum age. +|`path-pattern` |string |`{+}` |Updates the path prefix for this cross-origin config. |=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_smallrye_openapi_api_OpenApiConfig.adoc b/docs/config/io_smallrye_openapi_api_OpenApiConfig.adoc new file mode 100644 index 00000000000..670867d10c7 --- /dev/null +++ b/docs/config/io_smallrye_openapi_api_OpenApiConfig.adoc @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.smallrye.openapi.api.OpenApiConfig +:keywords: helidon, config, io.smallrye.openapi.api.OpenApiConfig +:basic-table-intro: The table below lists the configuration keys that configure io.smallrye.openapi.api.OpenApiConfig +include::{rootdir}/includes/attributes.adoc[] + += io.smallrye.openapi.api.OpenApiConfig Configuration + +// tag::config[] + + +Type: io.smallrye.openapi.api.OpenApiConfig + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. +|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. +|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. +|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. +|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. +|`servers` |string[] |{nbsp} |Sets servers. +|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. +|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/mp/health.adoc b/docs/mp/health.adoc index ac8899cef69..1f0c18c026c 100644 --- a/docs/mp/health.adoc +++ b/docs/mp/health.adoc @@ -152,7 +152,7 @@ Health checks may be configured using the following properties. The class responsible for configuration is: -include::{rootdir}/includes/config/io.helidon.health.HealthSupport.adoc[leveloffset=-1] +include::{rootdir}/config/io_helidon_health_HealthSupport.adoc[leveloffset=+1,tag=config] Current properties may be set in `application.yaml` or in `microprofile-config.properties` with `health` prefix. diff --git a/docs/mp/openapi.adoc b/docs/mp/openapi.adoc index e3476b678fc..4ebce5249b4 100644 --- a/docs/mp/openapi.adoc +++ b/docs/mp/openapi.adoc @@ -168,7 +168,7 @@ In addition, a client can specify the HTTP header `Accept:` as either `applicati Helidon OpenAPI configuration supports the following settings: -include::{rootdir}/includes/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc[leveloffset=-1] +include::{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[leveloffset=+1,tag=config] == Examples diff --git a/docs/se/webserver.adoc b/docs/se/webserver.adoc index 21cecdd9f0d..bd5bab63e80 100644 --- a/docs/se/webserver.adoc +++ b/docs/se/webserver.adoc @@ -104,7 +104,7 @@ just use `Config.create()` === Configuration options -include::{rootdir}/includes/config/io.helidon.webserver.WebServer.adoc[] +include::{rootdir}/config/io_helidon_webserver_WebServer.adoc[leveloffset=+2,tag=config] == Routing [[routing]] Routing lets you use request matching criteria to bind requests to a `handler` that implements @@ -1061,7 +1061,7 @@ server: === Configuration options -include::{rootdir}/includes/config/io.helidon.webserver.WebServerTls.adoc[] +include::{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[leveloffset=+2,tag=config] == HTTP Compression diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 60bee52a7ec..c01bbb8320a 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -530,6 +530,56 @@ backend: glyph: type: "icon" value: "library_books" + - type: "MENU" + title: "Config Reference" + dir: "config" + glyph: + type: "icon" + value: "library_books" + sources: + - "io_helidon_security_providers_abac_AbacProvider.adoc" + - "io_helidon_metrics_api_BaseMetricsSettings.adoc" + - "io_helidon_faulttolerance_Bulkhead.adoc" + - "io_helidon_faulttolerance_CircuitBreaker.adoc" + - "io_helidon_metrics_api_ComponentMetricsSettings.adoc" + - "io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc" + - "io_helidon_webserver_cors_CrossOriginConfig.adoc" + - "io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc" + - "io_helidon_health_HealthSupport.adoc" + - "io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc" + - "io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc" + - "io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc" + - "io_helidon_common_pki_KeyConfig.adoc" + - "io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc" + - "io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc" + - "io_helidon_common_configurable_LruCache.adoc" + - "io_helidon_microprofile_openapi_MPOpenAPISupport.adoc" + - "io_helidon_metrics_api_MetricsSettings.adoc" + - "io_helidon_security_providers_oidc_common_OidcConfig.adoc" + - "io_helidon_security_providers_oidc_OidcProvider.adoc" + - "io_helidon_openapi_OpenAPISupport.adoc" + - "io_helidon_security_providers_common_OutboundConfig.adoc" + - "io_helidon_security_providers_common_OutboundTarget.adoc" + - "io_helidon_common_pki_KeyConfig_PemBuilder.adoc" + - "io_helidon_metrics_api_RegistryFilterSettings.adoc" + - "io_helidon_metrics_api_RegistrySettings.adoc" + - "io_helidon_common_configurable_Resource.adoc" + - "io_helidon_servicecommon_rest_RestServiceSettings.adoc" + - "io_helidon_faulttolerance_Retry.adoc" + - "io_helidon_openapi_SEOpenAPISupport.adoc" + - "io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc" + - "io_helidon_security_Security.adoc" + - "io_helidon_security_SecurityTime.adoc" + - "io_helidon_webserver_SocketConfiguration.adoc" + - "io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc" + - "io_helidon_common_configurable_ThreadPoolSupplier.adoc" + - "io_helidon_faulttolerance_Timeout.adoc" + - "io_helidon_security_util_TokenHandler.adoc" + - "io_helidon_tracing_Tracer.adoc" + - "io_helidon_tracing_TracerBuilder.adoc" + - "io_helidon_webserver_WebServer.adoc" + - "io_helidon_webserver_WebServerTls.adoc" + - "io_smallrye_openapi_api_OpenApiConfig.adoc" - type: "PAGE" title: "Community" source: "community.adoc" diff --git a/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java b/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java index b555b1e89c0..2eb95989894 100644 --- a/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java +++ b/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java @@ -83,7 +83,7 @@ public Retry build() { * @param policy retry policy * @return updated builder instance */ - @ConfiguredOption(kind = ConfiguredOption.Kind.MAP) + @ConfiguredOption(provider = true) public Builder retryPolicy(RetryPolicy policy) { this.retryPolicy = policy; return this; diff --git a/health/health/src/main/java/io/helidon/health/HealthSupport.java b/health/health/src/main/java/io/helidon/health/HealthSupport.java index ed2f2061cbf..44901fb0293 100644 --- a/health/health/src/main/java/io/helidon/health/HealthSupport.java +++ b/health/health/src/main/java/io/helidon/health/HealthSupport.java @@ -304,7 +304,7 @@ public static HealthSupport create(Config config) { /** * Fluent API builder for {@link io.helidon.health.HealthSupport}. */ - @Configured(prefix = Builder.HEALTH_CONFIG_KEY) + @Configured(prefix = Builder.HEALTH_CONFIG_KEY, root = true) public static final class Builder extends HelidonRestServiceSupport.Builder { /** diff --git a/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java b/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java index 2e41dad960f..e86c038e860 100644 --- a/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java +++ b/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java @@ -193,7 +193,7 @@ interface Builder extends io.helidon.common.Builder { * @return updated builder */ @ConfiguredOption(key = KeyPerformanceIndicatorMetricsSettings.Builder.KEY_PERFORMANCE_INDICATORS_CONFIG_KEY, - kind = ConfiguredOption.Kind.MAP) + type = KeyPerformanceIndicatorMetricsSettings.class) Builder keyPerformanceIndicatorSettings(KeyPerformanceIndicatorMetricsSettings.Builder kpiSettings); /** @@ -203,7 +203,7 @@ interface Builder extends io.helidon.common.Builder { * @return updated builder */ @ConfiguredOption(key = BASE_CONFIG_KEY, - kind = ConfiguredOption.Kind.MAP) + type = BaseMetricsSettings.class) Builder baseMetricsSettings(BaseMetricsSettings.Builder baseMetricsSettingsBuilder); /** diff --git a/metrics/api/src/main/java/io/helidon/metrics/api/RegistrySettings.java b/metrics/api/src/main/java/io/helidon/metrics/api/RegistrySettings.java index d01315bb0c9..a315d488ac8 100644 --- a/metrics/api/src/main/java/io/helidon/metrics/api/RegistrySettings.java +++ b/metrics/api/src/main/java/io/helidon/metrics/api/RegistrySettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -116,6 +116,7 @@ interface Builder extends io.helidon.common.Builder { */ @ConfiguredOption( key = FILTER_CONFIG_KEY, + type = RegistryFilterSettings.class, description = "Name filtering, featuring optional exclude and include settings") Builder filterSettings(RegistryFilterSettings.Builder registryFilterSettingsBuilder); From 80281d4a887d9500a8f01c2aa5afab913e0e85d5 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 14 Jul 2022 20:05:10 +0200 Subject: [PATCH 18/51] Config reference page (#4546) Fixed javadocs links Signed-off-by: Tomas Langer --- docs/config/config_reference.adoc | 70 +++++++++++++++++++ ...microprofile.openapi.MPOpenAPIBuilder.adoc | 2 +- ...on.tracing.jaeger.JaegerTracerBuilder.adoc | 2 +- ..._helidon_common_configurable_LruCache.adoc | 2 +- ..._helidon_common_configurable_Resource.adoc | 2 +- ...figurable_ScheduledThreadPoolSupplier.adoc | 2 +- ...ommon_configurable_ThreadPoolSupplier.adoc | 2 +- .../io_helidon_common_pki_KeyConfig.adoc | 2 +- ..._common_pki_KeyConfig_KeystoreBuilder.adoc | 2 +- ...lidon_common_pki_KeyConfig_PemBuilder.adoc | 2 +- .../io_helidon_faulttolerance_Bulkhead.adoc | 2 +- ...helidon_faulttolerance_CircuitBreaker.adoc | 2 +- .../io_helidon_faulttolerance_Retry.adoc | 2 +- ...lttolerance_Retry_DelayingRetryPolicy.adoc | 2 +- ...aulttolerance_Retry_JitterRetryPolicy.adoc | 2 +- .../io_helidon_faulttolerance_Timeout.adoc | 2 +- .../io_helidon_health_HealthSupport.adoc | 2 +- .../io_helidon_media_common_MediaContext.adoc | 5 +- ...lidon_metrics_api_BaseMetricsSettings.adoc | 2 +- ..._metrics_api_ComponentMetricsSettings.adoc | 2 +- ...eyPerformanceIndicatorMetricsSettings.adoc | 2 +- ...o_helidon_metrics_api_MetricsSettings.adoc | 2 +- ...on_metrics_api_RegistryFilterSettings.adoc | 2 +- ..._helidon_metrics_api_RegistrySettings.adoc | 2 +- ...microprofile_openapi_MPOpenAPISupport.adoc | 2 +- .../io_helidon_openapi_OpenAPISupport.adoc | 2 +- .../io_helidon_openapi_SEOpenAPISupport.adoc | 2 +- docs/config/io_helidon_security_Security.adoc | 2 +- .../io_helidon_security_SecurityTime.adoc | 2 +- ..._security_providers_abac_AbacProvider.adoc | 2 +- ...urity_providers_common_OutboundConfig.adoc | 2 +- ...urity_providers_common_OutboundTarget.adoc | 2 +- ...s_httpauth_ConfigUserStore_ConfigUser.adoc | 2 +- ...viders_httpauth_HttpBasicAuthProvider.adoc | 2 +- ..._security_providers_oidc_OidcProvider.adoc | 2 +- ...rity_providers_oidc_common_OidcConfig.adoc | 2 +- ...io_helidon_security_util_TokenHandler.adoc | 2 +- ...common_rest_HelidonRestServiceSupport.adoc | 2 +- ...ervicecommon_rest_RestServiceSettings.adoc | 2 +- docs/config/io_helidon_tracing_Tracer.adoc | 2 +- .../io_helidon_tracing_TracerBuilder.adoc | 2 +- ...helidon_webserver_SocketConfiguration.adoc | 2 +- ...figuration_SocketConfigurationBuilder.adoc | 2 +- .../io_helidon_webserver_WebServer.adoc | 2 +- .../io_helidon_webserver_WebServerTls.adoc | 2 +- ...idon_webserver_cors_CrossOriginConfig.adoc | 2 +- docs/sitegen.yaml | 48 +------------ 47 files changed, 119 insertions(+), 92 deletions(-) create mode 100644 docs/config/config_reference.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc new file mode 100644 index 00000000000..be0c3918667 --- /dev/null +++ b/docs/config/config_reference.adoc @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration Reference +:keywords: helidon, config, reference + += Configuration Reference + +The following section lists all configurable types in Helidon. + +- xref:{rootdir}/config/io_helidon_security_providers_abac_AbacProvider.adoc[AbacProvider.adoc (security.providers.abac)] +- xref:{rootdir}/config/io_helidon_metrics_api_BaseMetricsSettings.adoc[BaseMetricsSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Bulkhead.adoc[Bulkhead.adoc (faulttolerance)] +- xref:{rootdir}/config/io_helidon_faulttolerance_CircuitBreaker.adoc[CircuitBreaker.adoc (faulttolerance)] +- xref:{rootdir}/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc[ComponentMetricsSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser.adoc (security.providers.httpauth.ConfigUserStore)] +- xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig.adoc (webserver.cors)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc[DelayingRetryPolicy.adoc (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_health_HealthSupport.adoc[HealthSupport.adoc (health)] +- xref:{rootdir}/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc[HelidonRestServiceSupport.adoc (servicecommon.rest)] +- xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[HttpBasicAuthProvider.adoc (security.providers.httpauth)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc[JitterRetryPolicy.adoc (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig.adoc (common.pki)] +- xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc[KeystoreBuilder.adoc (common.pki.KeyConfig)] +- xref:{rootdir}/config/io_helidon_common_configurable_LruCache.adoc[LruCache.adoc (common.configurable)] +- xref:{rootdir}/config/io_helidon_media_common_MediaContext.adoc[MediaContext (media.common)] +- xref:{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[MPOpenAPISupport.adoc (microprofile.openapi)] +- xref:{rootdir}/config/io_helidon_metrics_api_MetricsSettings.adoc[MetricsSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig.adoc (security.providers.oidc.common)] +- xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[OidcProvider.adoc (security.providers.oidc)] +- xref:{rootdir}/config/io_helidon_openapi_OpenAPISupport.adoc[OpenAPISupport.adoc (openapi)] +- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig.adoc (security.providers.common)] +- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget.adoc (security.providers.common)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc[PemBuilder.adoc (common.pki.KeyConfig)] +- xref:{rootdir}/config/io_helidon_metrics_api_RegistryFilterSettings.adoc[RegistryFilterSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[RegistrySettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource.adoc (common.configurable)] +- xref:{rootdir}/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc[RestServiceSettings.adoc (servicecommon.rest)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry.adoc[Retry.adoc (faulttolerance)] +- xref:{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[SEOpenAPISupport.adoc (openapi)] +- xref:{rootdir}/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc[ScheduledThreadPoolSupplier.adoc (common.configurable)] +- xref:{rootdir}/config/io_helidon_security_Security.adoc[Security.adoc (security)] +- xref:{rootdir}/config/io_helidon_security_SecurityTime.adoc[SecurityTime.adoc (security)] +- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration.adoc[SocketConfiguration.adoc (webserver)] +- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc[SocketConfigurationBuilder.adoc (webserver.SocketConfiguration)] +- xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc[ThreadPoolSupplier.adoc (common.configurable)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Timeout.adoc[Timeout.adoc (faulttolerance)] +- xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler.adoc (security.util)] +- xref:{rootdir}/config/io_helidon_tracing_Tracer.adoc[Tracer.adoc (tracing)] +- xref:{rootdir}/config/io_helidon_tracing_TracerBuilder.adoc[TracerBuilder.adoc (tracing)] +- xref:{rootdir}/config/io_helidon_webserver_WebServer.adoc[WebServer.adoc (webserver)] +- xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls.adoc (webserver)] +- xref:{rootdir}/config/io_smallrye_openapi_api_OpenApiConfig.adoc[io_smallrye_openapi_api_OpenApiConfig.adoc] diff --git a/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc b/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc index f37f56240ac..80e4958fd77 100644 --- a/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc +++ b/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io.helidon.microprofile.openapi.MPOpenAPISupport[io.helidon.microprofile.openapi.MPOpenAPISupport] +Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io/helidon/microprofile/openapi/MPOpenAPISupport.html[io.helidon.microprofile.openapi.MPOpenAPISupport] [source,text] diff --git a/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc b/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc index 404836eda57..5e3648fa503 100644 --- a/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc +++ b/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Jaeger tracer configuration. -Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.Tracer[io.helidon.tracing.Tracer] +Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/Tracer.html[io.helidon.tracing.Tracer] This is a standalone configuration type, prefix from configuration root: `tracing` diff --git a/docs/config/io_helidon_common_configurable_LruCache.adoc b/docs/config/io_helidon_common_configurable_LruCache.adoc index ec9a75e7551..676d8b15eaf 100644 --- a/docs/config/io_helidon_common_configurable_LruCache.adoc +++ b/docs/config/io_helidon_common_configurable_LruCache.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.LruCache[io.helidon.common.configurable.LruCache] +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/configurable/LruCache.html[io.helidon.common.configurable.LruCache] diff --git a/docs/config/io_helidon_common_configurable_Resource.adoc b/docs/config/io_helidon_common_configurable_Resource.adoc index f9b2b0513a3..087cc7d1884 100644 --- a/docs/config/io_helidon_common_configurable_Resource.adoc +++ b/docs/config/io_helidon_common_configurable_Resource.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.Resource[io.helidon.common.configurable.Resource] +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/configurable/Resource.html[io.helidon.common.configurable.Resource] diff --git a/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc b/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc index 62aa4415b44..0d3b5075ea3 100644 --- a/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc +++ b/docs/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.ScheduledThreadPoolSupplier[io.helidon.common.configurable.ScheduledThreadPoolSupplier] +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/configurable/ScheduledThreadPoolSupplier.html[io.helidon.common.configurable.ScheduledThreadPoolSupplier] diff --git a/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc b/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc index 6830bbd3d1a..554804bf775 100644 --- a/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc +++ b/docs/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.configurable/io.helidon.common.configurable.ThreadPoolSupplier[io.helidon.common.configurable.ThreadPoolSupplier] +Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/configurable/ThreadPoolSupplier.html[io.helidon.common.configurable.ThreadPoolSupplier] diff --git a/docs/config/io_helidon_common_pki_KeyConfig.adoc b/docs/config/io_helidon_common_pki_KeyConfig.adoc index ed3281c5b68..992fc7bc156 100644 --- a/docs/config/io_helidon_common_pki_KeyConfig.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.pki/io.helidon.common.pki.KeyConfig[io.helidon.common.pki.KeyConfig] +Type: link:{javadoc-base-url}/io.helidon.common.pki/io/helidon/common/pki/KeyConfig.html[io.helidon.common.pki.KeyConfig] diff --git a/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc b/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc index f43a92298ab..ec9123e5a37 100644 --- a/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io.helidon.common.pki.KeyConfig.KeystoreBuilder[io.helidon.common.pki.KeyConfig.KeystoreBuilder] +Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io/helidon/common/pki/KeyConfig/KeystoreBuilder.html[io.helidon.common.pki.KeyConfig.KeystoreBuilder] diff --git a/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc b/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc index 24a817f54be..7629975aa28 100644 --- a/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc +++ b/docs/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io.helidon.common.pki.KeyConfig.PemBuilder[io.helidon.common.pki.KeyConfig.PemBuilder] +Type: link:{javadoc-base-url}/io.helidon.common.pki.KeyConfig/io/helidon/common/pki/KeyConfig/PemBuilder.html[io.helidon.common.pki.KeyConfig.PemBuilder] diff --git a/docs/config/io_helidon_faulttolerance_Bulkhead.adoc b/docs/config/io_helidon_faulttolerance_Bulkhead.adoc index dc2d2095d6e..8810d60a670 100644 --- a/docs/config/io_helidon_faulttolerance_Bulkhead.adoc +++ b/docs/config/io_helidon_faulttolerance_Bulkhead.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Bulkhead[io.helidon.faulttolerance.Bulkhead] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io/helidon/faulttolerance/Bulkhead.html[io.helidon.faulttolerance.Bulkhead] diff --git a/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc b/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc index 3bc2278e15a..0539188cf3a 100644 --- a/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc +++ b/docs/config/io_helidon_faulttolerance_CircuitBreaker.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.CircuitBreaker[io.helidon.faulttolerance.CircuitBreaker] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io/helidon/faulttolerance/CircuitBreaker.html[io.helidon.faulttolerance.CircuitBreaker] diff --git a/docs/config/io_helidon_faulttolerance_Retry.adoc b/docs/config/io_helidon_faulttolerance_Retry.adoc index ec3ad69b600..5ac4c51d170 100644 --- a/docs/config/io_helidon_faulttolerance_Retry.adoc +++ b/docs/config/io_helidon_faulttolerance_Retry.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Retry[io.helidon.faulttolerance.Retry] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io/helidon/faulttolerance/Retry.html[io.helidon.faulttolerance.Retry] diff --git a/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc b/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc index 706052f8652..45639c70a88 100644 --- a/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc +++ b/docs/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io.helidon.faulttolerance.Retry.DelayingRetryPolicy[io.helidon.faulttolerance.Retry.DelayingRetryPolicy] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io/helidon/faulttolerance/Retry/DelayingRetryPolicy.html[io.helidon.faulttolerance.Retry.DelayingRetryPolicy] diff --git a/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc b/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc index 49f566bce16..c9189e35d8d 100644 --- a/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc +++ b/docs/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io.helidon.faulttolerance.Retry.JitterRetryPolicy[io.helidon.faulttolerance.Retry.JitterRetryPolicy] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance.Retry/io/helidon/faulttolerance/Retry/JitterRetryPolicy.html[io.helidon.faulttolerance.Retry.JitterRetryPolicy] diff --git a/docs/config/io_helidon_faulttolerance_Timeout.adoc b/docs/config/io_helidon_faulttolerance_Timeout.adoc index 594b8df4337..da7b520204c 100644 --- a/docs/config/io_helidon_faulttolerance_Timeout.adoc +++ b/docs/config/io_helidon_faulttolerance_Timeout.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io.helidon.faulttolerance.Timeout[io.helidon.faulttolerance.Timeout] +Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io/helidon/faulttolerance/Timeout.html[io.helidon.faulttolerance.Timeout] diff --git a/docs/config/io_helidon_health_HealthSupport.adoc b/docs/config/io_helidon_health_HealthSupport.adoc index d08f1c762d6..b5120323c52 100644 --- a/docs/config/io_helidon_health_HealthSupport.adoc +++ b/docs/config/io_helidon_health_HealthSupport.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.health/io.helidon.health.HealthSupport[io.helidon.health.HealthSupport] +Type: link:{javadoc-base-url}/io.helidon.health/io/helidon/health/HealthSupport.html[io.helidon.health.HealthSupport] This is a standalone configuration type, prefix from configuration root: `health` diff --git a/docs/config/io_helidon_media_common_MediaContext.adoc b/docs/config/io_helidon_media_common_MediaContext.adoc index 900078362ce..cf6cabfbad2 100644 --- a/docs/config/io_helidon_media_common_MediaContext.adoc +++ b/docs/config/io_helidon_media_common_MediaContext.adoc @@ -22,10 +22,11 @@ ifndef::rootdir[:rootdir: {docdir}/..] :basic-table-intro: The table below lists the configuration keys that configure io.helidon.media.common.MediaContext include::{rootdir}/includes/attributes.adoc[] -= io.helidon.media.common.MediaContext += MediaContext (media.common) Configuration // tag::config[] -link:{javadoc-base-url}/io.helidon.media.common/io.helidon.media.common.MediaContext[io.helidon.media.common.MediaContext] + +Type: link:{javadoc-base-url}/io.helidon.media.common/io/helidon/media/common/MediaContext.html[io.helidon.media.common.MediaContext] diff --git a/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc index 276a1958171..e889e6744d8 100644 --- a/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc +++ b/docs/config/io_helidon_metrics_api_BaseMetricsSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.BaseMetricsSettings[io.helidon.metrics.api.BaseMetricsSettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/BaseMetricsSettings.html[io.helidon.metrics.api.BaseMetricsSettings] [source,text] diff --git a/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc index 65dc3cc17dd..85ab4d9e2ac 100644 --- a/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc +++ b/docs/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.ComponentMetricsSettings[io.helidon.metrics.api.ComponentMetricsSettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/ComponentMetricsSettings.html[io.helidon.metrics.api.ComponentMetricsSettings] [source,text] diff --git a/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc b/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc index ed508fa76b1..42bdfe62167 100644 --- a/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc +++ b/docs/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings[io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/KeyPerformanceIndicatorMetricsSettings.html[io.helidon.metrics.api.KeyPerformanceIndicatorMetricsSettings] [source,text] diff --git a/docs/config/io_helidon_metrics_api_MetricsSettings.adoc b/docs/config/io_helidon_metrics_api_MetricsSettings.adoc index 9b750232f28..e8f046b00dd 100644 --- a/docs/config/io_helidon_metrics_api_MetricsSettings.adoc +++ b/docs/config/io_helidon_metrics_api_MetricsSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.MetricsSettings[io.helidon.metrics.api.MetricsSettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/MetricsSettings.html[io.helidon.metrics.api.MetricsSettings] [source,text] diff --git a/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc b/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc index ab928b9bcee..77750d8b0dd 100644 --- a/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc +++ b/docs/config/io_helidon_metrics_api_RegistryFilterSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.RegistryFilterSettings[io.helidon.metrics.api.RegistryFilterSettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/RegistryFilterSettings.html[io.helidon.metrics.api.RegistryFilterSettings] diff --git a/docs/config/io_helidon_metrics_api_RegistrySettings.adoc b/docs/config/io_helidon_metrics_api_RegistrySettings.adoc index b54459fce1f..b8ad2481b73 100644 --- a/docs/config/io_helidon_metrics_api_RegistrySettings.adoc +++ b/docs/config/io_helidon_metrics_api_RegistrySettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.metrics.api/io.helidon.metrics.api.RegistrySettings[io.helidon.metrics.api.RegistrySettings] +Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/RegistrySettings.html[io.helidon.metrics.api.RegistrySettings] [source,text] diff --git a/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc index f37f56240ac..80e4958fd77 100644 --- a/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc +++ b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io.helidon.microprofile.openapi.MPOpenAPISupport[io.helidon.microprofile.openapi.MPOpenAPISupport] +Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io/helidon/microprofile/openapi/MPOpenAPISupport.html[io.helidon.microprofile.openapi.MPOpenAPISupport] [source,text] diff --git a/docs/config/io_helidon_openapi_OpenAPISupport.adoc b/docs/config/io_helidon_openapi_OpenAPISupport.adoc index dcce6c86ece..c8603523223 100644 --- a/docs/config/io_helidon_openapi_OpenAPISupport.adoc +++ b/docs/config/io_helidon_openapi_OpenAPISupport.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] OpenAPI support configuration -Type: link:{javadoc-base-url}/io.helidon.openapi/io.helidon.openapi.OpenAPISupport[io.helidon.openapi.OpenAPISupport] +Type: link:{javadoc-base-url}/io.helidon.openapi/io/helidon/openapi/OpenAPISupport.html[io.helidon.openapi.OpenAPISupport] diff --git a/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc index 58435c54b52..63983cf06cf 100644 --- a/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc +++ b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.openapi/io.helidon.openapi.SEOpenAPISupport[io.helidon.openapi.SEOpenAPISupport] +Type: link:{javadoc-base-url}/io.helidon.openapi/io/helidon/openapi/SEOpenAPISupport.html[io.helidon.openapi.SEOpenAPISupport] [source,text] diff --git a/docs/config/io_helidon_security_Security.adoc b/docs/config/io_helidon_security_Security.adoc index bdbec475e47..b578ae9428b 100644 --- a/docs/config/io_helidon_security_Security.adoc +++ b/docs/config/io_helidon_security_Security.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Configuration of security providers, integration and other security options -Type: link:{javadoc-base-url}/io.helidon.security/io.helidon.security.Security[io.helidon.security.Security] +Type: link:{javadoc-base-url}/io.helidon.security/io/helidon/security/Security.html[io.helidon.security.Security] This is a standalone configuration type, prefix from configuration root: `security` diff --git a/docs/config/io_helidon_security_SecurityTime.adoc b/docs/config/io_helidon_security_SecurityTime.adoc index e50f3afed00..cf48b430625 100644 --- a/docs/config/io_helidon_security_SecurityTime.adoc +++ b/docs/config/io_helidon_security_SecurityTime.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.security/io.helidon.security.SecurityTime[io.helidon.security.SecurityTime] +Type: link:{javadoc-base-url}/io.helidon.security/io/helidon/security/SecurityTime.html[io.helidon.security.SecurityTime] diff --git a/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc b/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc index a4a01b863bf..bdc32a3f390 100644 --- a/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc +++ b/docs/config/io_helidon_security_providers_abac_AbacProvider.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Attribute Based Access Control provider -Type: link:{javadoc-base-url}/io.helidon.security.providers.abac/io.helidon.security.providers.abac.AbacProvider[io.helidon.security.providers.abac.AbacProvider] +Type: link:{javadoc-base-url}/io.helidon.security.providers.abac/io/helidon/security/providers/abac/AbacProvider.html[io.helidon.security.providers.abac.AbacProvider] [source,text] diff --git a/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc b/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc index 9afe67df243..854ba5804ba 100644 --- a/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc +++ b/docs/config/io_helidon_security_providers_common_OutboundConfig.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Outbound configuration for outbound security -Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io.helidon.security.providers.common.OutboundConfig[io.helidon.security.providers.common.OutboundConfig] +Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io/helidon/security/providers/common/OutboundConfig.html[io.helidon.security.providers.common.OutboundConfig] diff --git a/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc b/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc index e9b84f9e8fd..b177c1ef52c 100644 --- a/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc +++ b/docs/config/io_helidon_security_providers_common_OutboundTarget.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io.helidon.security.providers.common.OutboundTarget[io.helidon.security.providers.common.OutboundTarget] +Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io/helidon/security/providers/common/OutboundTarget.html[io.helidon.security.providers.common.OutboundTarget] diff --git a/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc b/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc index 3993b19f82c..87677e5f818 100644 --- a/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc +++ b/docs/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth.ConfigUserStore/io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser[io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser] +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth.ConfigUserStore/io/helidon/security/providers/httpauth/ConfigUserStore/ConfigUser.html[io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser] diff --git a/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc index 2fd7ecb8bed..bc28b134477 100644 --- a/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc +++ b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] HTTP Basic Authentication provider -Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth/io.helidon.security.providers.httpauth.HttpBasicAuthProvider[io.helidon.security.providers.httpauth.HttpBasicAuthProvider] +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth/io/helidon/security/providers/httpauth/HttpBasicAuthProvider.html[io.helidon.security.providers.httpauth.HttpBasicAuthProvider] [source,text] diff --git a/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc index 8892884a022..76ec6b5671f 100644 --- a/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc +++ b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Open ID Connect security provider -Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc/io.helidon.security.providers.oidc.OidcProvider[io.helidon.security.providers.oidc.OidcProvider] +Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc/io/helidon/security/providers/oidc/OidcProvider.html[io.helidon.security.providers.oidc.OidcProvider] [source,text] diff --git a/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc index ab632102fe6..537cd3b88a4 100644 --- a/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc +++ b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Open ID Connect configuration -Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io.helidon.security.providers.oidc.common.OidcConfig[io.helidon.security.providers.oidc.common.OidcConfig] +Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helidon/security/providers/oidc/common/OidcConfig.html[io.helidon.security.providers.oidc.common.OidcConfig] diff --git a/docs/config/io_helidon_security_util_TokenHandler.adoc b/docs/config/io_helidon_security_util_TokenHandler.adoc index f6c69c39edc..fe6c1298740 100644 --- a/docs/config/io_helidon_security_util_TokenHandler.adoc +++ b/docs/config/io_helidon_security_util_TokenHandler.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.security.util/io.helidon.security.util.TokenHandler[io.helidon.security.util.TokenHandler] +Type: link:{javadoc-base-url}/io.helidon.security.util/io/helidon/security/util/TokenHandler.html[io.helidon.security.util.TokenHandler] diff --git a/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc b/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc index 8e2c810d283..022a09ebbcc 100644 --- a/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc +++ b/docs/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io.helidon.servicecommon.rest.HelidonRestServiceSupport[io.helidon.servicecommon.rest.HelidonRestServiceSupport] +Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io/helidon/servicecommon/rest/HelidonRestServiceSupport.html[io.helidon.servicecommon.rest.HelidonRestServiceSupport] diff --git a/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc b/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc index a30a4aa2f14..8ae7853a20d 100644 --- a/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc +++ b/docs/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io.helidon.servicecommon.rest.RestServiceSettings[io.helidon.servicecommon.rest.RestServiceSettings] +Type: link:{javadoc-base-url}/io.helidon.servicecommon.rest/io/helidon/servicecommon/rest/RestServiceSettings.html[io.helidon.servicecommon.rest.RestServiceSettings] diff --git a/docs/config/io_helidon_tracing_Tracer.adoc b/docs/config/io_helidon_tracing_Tracer.adoc index 404836eda57..5e3648fa503 100644 --- a/docs/config/io_helidon_tracing_Tracer.adoc +++ b/docs/config/io_helidon_tracing_Tracer.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Jaeger tracer configuration. -Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.Tracer[io.helidon.tracing.Tracer] +Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/Tracer.html[io.helidon.tracing.Tracer] This is a standalone configuration type, prefix from configuration root: `tracing` diff --git a/docs/config/io_helidon_tracing_TracerBuilder.adoc b/docs/config/io_helidon_tracing_TracerBuilder.adoc index 9b8e6cae1a4..c086d6fd179 100644 --- a/docs/config/io_helidon_tracing_TracerBuilder.adoc +++ b/docs/config/io_helidon_tracing_TracerBuilder.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] OpenTracing tracer configuration. -Type: link:{javadoc-base-url}/io.helidon.tracing/io.helidon.tracing.TracerBuilder[io.helidon.tracing.TracerBuilder] +Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/TracerBuilder.html[io.helidon.tracing.TracerBuilder] diff --git a/docs/config/io_helidon_webserver_SocketConfiguration.adoc b/docs/config/io_helidon_webserver_SocketConfiguration.adoc index bbdada32f96..23a2831805f 100644 --- a/docs/config/io_helidon_webserver_SocketConfiguration.adoc +++ b/docs/config/io_helidon_webserver_SocketConfiguration.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.SocketConfiguration[io.helidon.webserver.SocketConfiguration] +Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/SocketConfiguration.html[io.helidon.webserver.SocketConfiguration] diff --git a/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc b/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc index 9fcb486b0ad..948f1216a6e 100644 --- a/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc +++ b/docs/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.webserver.SocketConfiguration/io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder[io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder] +Type: link:{javadoc-base-url}/io.helidon.webserver.SocketConfiguration/io/helidon/webserver/SocketConfiguration/SocketConfigurationBuilder.html[io.helidon.webserver.SocketConfiguration.SocketConfigurationBuilder] diff --git a/docs/config/io_helidon_webserver_WebServer.adoc b/docs/config/io_helidon_webserver_WebServer.adoc index 276c49b17b6..d1433c7945a 100644 --- a/docs/config/io_helidon_webserver_WebServer.adoc +++ b/docs/config/io_helidon_webserver_WebServer.adoc @@ -29,7 +29,7 @@ include::{rootdir}/includes/attributes.adoc[] Configuration of the HTTP server. -Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.WebServer[io.helidon.webserver.WebServer] +Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/WebServer.html[io.helidon.webserver.WebServer] This is a standalone configuration type, prefix from configuration root: `server` diff --git a/docs/config/io_helidon_webserver_WebServerTls.adoc b/docs/config/io_helidon_webserver_WebServerTls.adoc index 45ddadd00e1..31fb77cbf30 100644 --- a/docs/config/io_helidon_webserver_WebServerTls.adoc +++ b/docs/config/io_helidon_webserver_WebServerTls.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.webserver/io.helidon.webserver.WebServerTls[io.helidon.webserver.WebServerTls] +Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/WebServerTls.html[io.helidon.webserver.WebServerTls] diff --git a/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc b/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc index 24a022742a9..3038d70c6ef 100644 --- a/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc +++ b/docs/config/io_helidon_webserver_cors_CrossOriginConfig.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -Type: link:{javadoc-base-url}/io.helidon.webserver.cors/io.helidon.webserver.cors.CrossOriginConfig[io.helidon.webserver.cors.CrossOriginConfig] +Type: link:{javadoc-base-url}/io.helidon.webserver.cors/io/helidon/webserver/cors/CrossOriginConfig.html[io.helidon.webserver.cors.CrossOriginConfig] diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index c01bbb8320a..629b4be9921 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -530,56 +530,12 @@ backend: glyph: type: "icon" value: "library_books" - - type: "MENU" + - type: "PAGE" title: "Config Reference" - dir: "config" + source: "config/config_reference.adoc" glyph: type: "icon" value: "library_books" - sources: - - "io_helidon_security_providers_abac_AbacProvider.adoc" - - "io_helidon_metrics_api_BaseMetricsSettings.adoc" - - "io_helidon_faulttolerance_Bulkhead.adoc" - - "io_helidon_faulttolerance_CircuitBreaker.adoc" - - "io_helidon_metrics_api_ComponentMetricsSettings.adoc" - - "io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc" - - "io_helidon_webserver_cors_CrossOriginConfig.adoc" - - "io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc" - - "io_helidon_health_HealthSupport.adoc" - - "io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc" - - "io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc" - - "io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc" - - "io_helidon_common_pki_KeyConfig.adoc" - - "io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc" - - "io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc" - - "io_helidon_common_configurable_LruCache.adoc" - - "io_helidon_microprofile_openapi_MPOpenAPISupport.adoc" - - "io_helidon_metrics_api_MetricsSettings.adoc" - - "io_helidon_security_providers_oidc_common_OidcConfig.adoc" - - "io_helidon_security_providers_oidc_OidcProvider.adoc" - - "io_helidon_openapi_OpenAPISupport.adoc" - - "io_helidon_security_providers_common_OutboundConfig.adoc" - - "io_helidon_security_providers_common_OutboundTarget.adoc" - - "io_helidon_common_pki_KeyConfig_PemBuilder.adoc" - - "io_helidon_metrics_api_RegistryFilterSettings.adoc" - - "io_helidon_metrics_api_RegistrySettings.adoc" - - "io_helidon_common_configurable_Resource.adoc" - - "io_helidon_servicecommon_rest_RestServiceSettings.adoc" - - "io_helidon_faulttolerance_Retry.adoc" - - "io_helidon_openapi_SEOpenAPISupport.adoc" - - "io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc" - - "io_helidon_security_Security.adoc" - - "io_helidon_security_SecurityTime.adoc" - - "io_helidon_webserver_SocketConfiguration.adoc" - - "io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc" - - "io_helidon_common_configurable_ThreadPoolSupplier.adoc" - - "io_helidon_faulttolerance_Timeout.adoc" - - "io_helidon_security_util_TokenHandler.adoc" - - "io_helidon_tracing_Tracer.adoc" - - "io_helidon_tracing_TracerBuilder.adoc" - - "io_helidon_webserver_WebServer.adoc" - - "io_helidon_webserver_WebServerTls.adoc" - - "io_smallrye_openapi_api_OpenApiConfig.adoc" - type: "PAGE" title: "Community" source: "community.adoc" From dec918f29ce0a7e9db23354dc1af212ef6741cc8 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 14 Jul 2022 22:53:19 +0200 Subject: [PATCH 19/51] Deserialization configuration update. (#4533) * Deserialization configuration update. * Custom configuration can now be done. Signed-off-by: Tomas Langer --- .../helidon/common/SerializationConfig.java | 116 ++++++++++-------- .../common/SerializationConfigTest.java | 2 +- tests/integration/jep290/pom.xml | 1 + .../jep290/server_and_custom/pom.xml | 56 +++++++++ .../serverandcustom/ConfiguredInBuilder.java | 33 +++++ .../jep290/serverandcustom/NotConfigured.java | 33 +++++ .../serverandcustom/CustomConfigTest.java | 111 +++++++++++++++++ .../jep290/setcfc/DeserializationTest.java | 4 +- 8 files changed, 304 insertions(+), 52 deletions(-) create mode 100644 tests/integration/jep290/server_and_custom/pom.xml create mode 100644 tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/ConfiguredInBuilder.java create mode 100644 tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/NotConfigured.java create mode 100644 tests/integration/jep290/server_and_custom/src/test/java/io/helidon/tests/integration/jep290/serverandcustom/CustomConfigTest.java diff --git a/common/common/src/main/java/io/helidon/common/SerializationConfig.java b/common/common/src/main/java/io/helidon/common/SerializationConfig.java index 4b6582931d6..1b37b152c79 100644 --- a/common/common/src/main/java/io/helidon/common/SerializationConfig.java +++ b/common/common/src/main/java/io/helidon/common/SerializationConfig.java @@ -126,7 +126,20 @@ public static Builder builder() { * This is a one-off call to set up global filter. */ public static void configureRuntime() { - builder().onNoConfig(Action.CONFIGURE).build().configure(); + builder().build().configureDefaults(); + } + + /* + Called from configureRuntime to ensure we set up at least the defaults (if no custom configuration is used) + */ + private void configureDefaults() { + if (EXISTING_CONFIG.compareAndSet(null, options)) { + // we should configure this instance as the global + doConfigure(); + } else { + LOGGER.log(Level.FINER, "Will not configure defaults, " + + "there is already a serialization config in place: " + EXISTING_CONFIG.get()); + } } /** @@ -139,52 +152,8 @@ public static void configureRuntime() { */ public void configure() { if (EXISTING_CONFIG.compareAndSet(null, options)) { - // this process is responsible for setting everything up, nobody else can reach this line - - ObjectInputFilter currentFilter = ObjectInputFilter.Config.getSerialFilter(); - - if (currentFilter == null) { - switch (options.onNoConfig()) { - case FAIL: - throw new IllegalStateException("There is no global serial filter configured. To automatically configure" - + " a filter, please set system property " + PROP_NO_CONFIG_ACTION - + " to \"configure\""); - case WARN: - AtomicBoolean logged = new AtomicBoolean(); - configureTracingFilter(options, it -> { - if (it.serialClass() != null && logged.compareAndSet(false, true)) { - LOGGER.warning("Deserialization attempted for class " + it.serialClass().getName() - + ", yet there is no global serial filter configured. " - + "To automatically configure" - + " a filter, please set system property \"" + PROP_NO_CONFIG_ACTION - + "\" to \"configure\""); - } - return ObjectInputFilter.Status.UNDECIDED; - }); - return; - case IGNORE: - LOGGER.finest("Ignoring that there is no global serial filter configured. To automatically configure" - + " a filter, please set system property " + PROP_NO_CONFIG_ACTION - + " to \"configure\""); - configureTracingFilter(options, null); - return; - default: - throw new IllegalArgumentException("Unsupported no configuration action: " + options.onNoConfig()); - case CONFIGURE: - // this is the only option that continues with execution - configureGlobalFilter(options); - break; - } - } else { - Action action = options.onWrongConfig(); - - if (action == Action.IGNORE) { - LOGGER.finest("Existing serialization config is ignored by Helidon."); - return; - } - - validateExistingFilter(currentFilter, action); - } + // we were explicitly asked to configure this instance + doConfigure(); } else { ConfigOptions existingOptions = EXISTING_CONFIG.get(); if (options.equals(existingOptions)) { @@ -200,6 +169,55 @@ ConfigOptions options() { return options; } + private void doConfigure() { + // this process is responsible for setting everything up, nobody else can reach this line + + ObjectInputFilter currentFilter = ObjectInputFilter.Config.getSerialFilter(); + + if (currentFilter == null) { + switch (options.onNoConfig()) { + case FAIL: + throw new IllegalStateException("There is no global serial filter configured. To automatically configure" + + " a filter, please set system property " + PROP_NO_CONFIG_ACTION + + " to \"configure\""); + case WARN: + AtomicBoolean logged = new AtomicBoolean(); + configureTracingFilter(options, it -> { + if (it.serialClass() != null && logged.compareAndSet(false, true)) { + LOGGER.warning("Deserialization attempted for class " + it.serialClass().getName() + + ", yet there is no global serial filter configured. " + + "To automatically configure" + + " a filter, please set system property \"" + PROP_NO_CONFIG_ACTION + + "\" to \"configure\""); + } + return ObjectInputFilter.Status.UNDECIDED; + }); + return; + case IGNORE: + LOGGER.finest("Ignoring that there is no global serial filter configured. To automatically configure" + + " a filter, please set system property " + PROP_NO_CONFIG_ACTION + + " to \"configure\""); + configureTracingFilter(options, null); + return; + default: + throw new IllegalArgumentException("Unsupported no configuration action: " + options.onNoConfig()); + case CONFIGURE: + // this is the only option that continues with execution + configureGlobalFilter(options); + break; + } + } else { + Action action = options.onWrongConfig(); + + if (action == Action.IGNORE) { + LOGGER.finest("Existing serialization config is ignored by Helidon."); + return; + } + + validateExistingFilter(currentFilter, action); + } + } + private void validateExistingFilter(ObjectInputFilter currentFilter, Action action) { String currentFilterString = System.getProperty("jdk.serialFilter"); @@ -367,7 +385,7 @@ public enum TraceOption { */ public static class Builder implements io.helidon.common.Builder { private Action onWrongConfig = configuredAction(PROP_WRONG_CONFIG_ACTION, Action.FAIL); - private Action onNoConfig = configuredAction(PROP_NO_CONFIG_ACTION, Action.FAIL); + private Action onNoConfig = configuredAction(PROP_NO_CONFIG_ACTION, Action.CONFIGURE); private String filterPattern = System.getProperty(PROP_PATTERN); private TraceOption traceSerialization = configuredTrace(TraceOption.NONE); private boolean ignoreFiles = Boolean.getBoolean(PROP_IGNORE_FILES); @@ -437,7 +455,7 @@ public Builder onWrongConfig(Action onWrongConfig) { /** * What action to do in case of no configuration of the global filter. * - * @param onNoConfig action to do + * @param onNoConfig action to do, defaults to {@link io.helidon.common.SerializationConfig.Action#CONFIGURE} * @return updated builder */ public Builder onNoConfig(Action onNoConfig) { diff --git a/common/common/src/test/java/io/helidon/common/SerializationConfigTest.java b/common/common/src/test/java/io/helidon/common/SerializationConfigTest.java index c182e27b27d..d154b264360 100644 --- a/common/common/src/test/java/io/helidon/common/SerializationConfigTest.java +++ b/common/common/src/test/java/io/helidon/common/SerializationConfigTest.java @@ -34,7 +34,7 @@ void testDefaults() { SerializationConfig.ConfigOptions options = serializationConfig.options(); assertThat(options.traceSerialization(), is(SerializationConfig.TraceOption.NONE)); - assertThat(options.onNoConfig(), is(SerializationConfig.Action.FAIL)); + assertThat(options.onNoConfig(), is(SerializationConfig.Action.CONFIGURE)); assertThat(options.onWrongConfig(), is(SerializationConfig.Action.FAIL)); assertThat(options.filterPattern(), is("!*")); } diff --git a/tests/integration/jep290/pom.xml b/tests/integration/jep290/pom.xml index 4061b47c589..17875bb1aac 100644 --- a/tests/integration/jep290/pom.xml +++ b/tests/integration/jep290/pom.xml @@ -77,5 +77,6 @@ set_c_f_c set_f set_o + server_and_custom diff --git a/tests/integration/jep290/server_and_custom/pom.xml b/tests/integration/jep290/server_and_custom/pom.xml new file mode 100644 index 00000000000..b03e7b48487 --- /dev/null +++ b/tests/integration/jep290/server_and_custom/pom.xml @@ -0,0 +1,56 @@ + + + + + 4.0.0 + + + io.helidon.tests.integration.jep290 + helidon-tests-integration-jep290-project + 3.0.0-SNAPSHOT + + + helidon-tests-integration-jep290-server-and-custom + Helidon Tests Integration JEP-290 server_and_custom + + + Use both WebServer and a custom Serialization config + + + + + io.helidon.common + helidon-common + + + io.helidon.webserver + helidon-webserver + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + diff --git a/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/ConfiguredInBuilder.java b/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/ConfiguredInBuilder.java new file mode 100644 index 00000000000..061b8ef015a --- /dev/null +++ b/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/ConfiguredInBuilder.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, 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. + */ + +package io.helidon.tests.integration.jep290.serverandcustom; + +import java.io.Serializable; + +class ConfiguredInBuilder implements Serializable { + private static final long serialVersionUID = 12L; + + private final String text; + + ConfiguredInBuilder(String text) { + this.text = text; + } + + String text() { + return text; + } +} diff --git a/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/NotConfigured.java b/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/NotConfigured.java new file mode 100644 index 00000000000..1c4ed014df6 --- /dev/null +++ b/tests/integration/jep290/server_and_custom/src/main/java/io/helidon/tests/integration/jep290/serverandcustom/NotConfigured.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, 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. + */ + +package io.helidon.tests.integration.jep290.serverandcustom; + +import java.io.Serializable; + +class NotConfigured implements Serializable { + private static final long serialVersionUID = 12L; + + private String text; + + NotConfigured(String text) { + this.text = text; + } + + String text() { + return text; + } +} diff --git a/tests/integration/jep290/server_and_custom/src/test/java/io/helidon/tests/integration/jep290/serverandcustom/CustomConfigTest.java b/tests/integration/jep290/server_and_custom/src/test/java/io/helidon/tests/integration/jep290/serverandcustom/CustomConfigTest.java new file mode 100644 index 00000000000..3fd844c7af6 --- /dev/null +++ b/tests/integration/jep290/server_and_custom/src/test/java/io/helidon/tests/integration/jep290/serverandcustom/CustomConfigTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021, 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. + */ + +package io.helidon.tests.integration.jep290.serverandcustom; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.time.Duration; +import java.util.Random; + +import io.helidon.common.SerializationConfig; +import io.helidon.webserver.WebServer; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CustomConfigTest { + private static WebServer webServer; + private static String testString; + + @BeforeAll + static void init() { + // first set up deserialization filter using a builder + SerializationConfig.builder() + .traceSerialization(SerializationConfig.TraceOption.FULL) + .filterPattern(ConfiguredInBuilder.class.getName()) + .build() + .configure(); + + testString = "Hello_" + new Random().nextInt(10); + + // then start web server + webServer = WebServer.builder() + .build() + .start() + .await(Duration.ofSeconds(10)); + } + + @AfterAll + static void stop() { + if (webServer != null) { + webServer.shutdown() + .await(Duration.ofSeconds(5)); + } + } + + @Test + void testConfigured() throws IOException, ClassNotFoundException { + ConfiguredInBuilder object = new ConfiguredInBuilder(testString); + + ByteArrayOutputStream ob = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(ob); + + oos.writeObject(object); + oos.close(); + + byte[] bytes = ob.toByteArray(); + + ByteArrayInputStream ib = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(ib); + Object o = ois.readObject(); + + assertThat(o, CoreMatchers.instanceOf(ConfiguredInBuilder.class)); + + object = (ConfiguredInBuilder) o; + + assertThat(object.text(), is(testString)); + } + + @Test + void testNotConfigured() throws IOException { + NotConfigured object = new NotConfigured(testString); + + ByteArrayOutputStream ob = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(ob); + + oos.writeObject(object); + oos.close(); + + byte[] bytes = ob.toByteArray(); + + ByteArrayInputStream ib = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(ib); + + assertThrows(InvalidClassException.class, ois::readObject); + + } +} diff --git a/tests/integration/jep290/set_c_f_c/src/test/java/io/helidon/tests/integration/jep290/setcfc/DeserializationTest.java b/tests/integration/jep290/set_c_f_c/src/test/java/io/helidon/tests/integration/jep290/setcfc/DeserializationTest.java index 02d5525888f..c1b351f19d1 100644 --- a/tests/integration/jep290/set_c_f_c/src/test/java/io/helidon/tests/integration/jep290/setcfc/DeserializationTest.java +++ b/tests/integration/jep290/set_c_f_c/src/test/java/io/helidon/tests/integration/jep290/setcfc/DeserializationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -98,7 +98,7 @@ void testConfiguredInPattern() throws IOException, ClassNotFoundException { } @Test - void testNotConfigured() throws IOException, ClassNotFoundException { + void testNotConfigured() throws IOException { NotConfigured object = new NotConfigured(testString); ByteArrayOutputStream ob = new ByteArrayOutputStream(); From ab99a7b2ae749b5b574e61dc367824543b9cd4cc Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 14 Jul 2022 22:53:32 +0200 Subject: [PATCH 20/51] Add module info to native image extensions. (#4529) Signed-off-by: Tomas Langer --- .../src/main/java/module-info.java | 28 +++++++++++++++++++ .../src/main/java/module-info.java | 28 +++++++++++++++++++ .../META-INF/microprofile-config.properties | 1 + 3 files changed, 57 insertions(+) create mode 100644 integrations/graal/mp-native-image-extension/src/main/java/module-info.java create mode 100644 integrations/graal/native-image-extension/src/main/java/module-info.java diff --git a/integrations/graal/mp-native-image-extension/src/main/java/module-info.java b/integrations/graal/mp-native-image-extension/src/main/java/module-info.java new file mode 100644 index 00000000000..6b7f46287fc --- /dev/null +++ b/integrations/graal/mp-native-image-extension/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/** + * Extension for Graal VM native image to correctly build Helidon MicroProfile applications. + */ +module io.helidon.graal.nativeimage.mp { + requires jakarta.cdi; + requires svm; + requires io.helidon.graal.nativeimage; + requires jakarta.json; + requires org.graalvm.sdk; + requires weld.core.impl; + + exports io.helidon.integrations.graal.mp.nativeimage.extension; +} \ No newline at end of file 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 new file mode 100644 index 00000000000..7afa0597e82 --- /dev/null +++ b/integrations/graal/native-image-extension/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/** + * Extension for Graal VM native image to correctly build Helidon applications. + */ +module io.helidon.graal.nativeimage { + requires io.helidon.config; + requires io.github.classgraph; + requires io.helidon.config.mp; + requires svm; + requires org.graalvm.sdk; + requires jakarta.json; + + exports io.helidon.integrations.graal.nativeimage.extension; +} \ No newline at end of file diff --git a/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties b/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties index 56117ce7648..d40065e877f 100644 --- a/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties +++ b/tests/integration/native-image/mp-1/src/main/resources/META-INF/microprofile-config.properties @@ -23,6 +23,7 @@ mp.jwt.verify.publickey.location=/verify-jwk.json mp.jwt.verify.issuer=native-image-mp1 tracing.service=mp1 +tracing.global=false features.print-details=true From d4290e1cee813603a90b9f872b2073c4b159b531 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Thu, 14 Jul 2022 20:26:17 -0500 Subject: [PATCH 21/51] Update MP metrics doc (#4536) --- docs/config/config_reference.adoc | 2 + ...microprofile.openapi.MPOpenAPIBuilder.adoc | 70 -- ...pi.internal.OpenAPIConfigImpl.Builder.adoc | 56 -- ...on.tracing.jaeger.JaegerTracerBuilder.adoc | 67 -- ...grations_micrometer_MicrometerSupport.adoc | 57 ++ ...don_metrics_serviceapi_MetricsSupport.adoc | 57 ++ docs/includes/attributes.adoc | 6 + docs/includes/guides/metrics.adoc | 2 +- .../metrics/metrics-capable-components.adoc | 54 +- docs/includes/metrics/metrics-config.adoc | 168 +++++ docs/includes/metrics/micrometer-shared.adoc | 81 ++- .../metrics/prometheus-exemplar-support.adoc | 134 ++-- docs/includes/pages.adoc | 2 +- docs/mp/introduction/introduction.adoc | 2 +- docs/mp/metrics/introduction.adoc | 49 -- .../metrics/metrics-capable-components.adoc | 11 +- docs/mp/metrics/metrics.adoc | 634 ++++++++++++++++++ docs/mp/metrics/micrometer.adoc | 57 +- .../metrics/prometheus-exemplar-support.adoc | 4 +- docs/mp/tracing.adoc | 2 + docs/se/tracing/tracing.adoc | 2 + docs/sitegen.yaml | 4 +- integrations/micrometer/micrometer/pom.xml | 12 + .../micrometer/MicrometerSupport.java | 2 + .../micrometer/src/main/java/module-info.java | 4 +- .../helidon/metrics/api/MetricsSettings.java | 2 +- metrics/service-api/pom.xml | 12 + .../metrics/serviceapi/MetricsSupport.java | 7 +- 28 files changed, 1226 insertions(+), 334 deletions(-) delete mode 100644 docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc delete mode 100644 docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc delete mode 100644 docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc create mode 100644 docs/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc create mode 100644 docs/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc create mode 100644 docs/includes/metrics/metrics-config.adoc delete mode 100644 docs/mp/metrics/introduction.adoc create mode 100644 docs/mp/metrics/metrics.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index be0c3918667..b74a015de9d 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -43,6 +43,8 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_media_common_MediaContext.adoc[MediaContext (media.common)] - xref:{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[MPOpenAPISupport.adoc (microprofile.openapi)] - xref:{rootdir}/config/io_helidon_metrics_api_MetricsSettings.adoc[MetricsSettings.adoc (metrics.api)] +- xref:{rootdir}/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc[MetricsSupport.adoc (metrics.serviceapi)] +- xref:{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[MicrometerSupport.adoc (integrations.micrometer)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig.adoc (security.providers.oidc.common)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[OidcProvider.adoc (security.providers.oidc)] - xref:{rootdir}/config/io_helidon_openapi_OpenAPISupport.adoc[OpenAPISupport.adoc (openapi)] diff --git a/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc b/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc deleted file mode 100644 index 80e4958fd77..00000000000 --- a/docs/config/io.helidon.microprofile.openapi.MPOpenAPIBuilder.adoc +++ /dev/null @@ -1,70 +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. - -/////////////////////////////////////////////////////////////////////////////// - -ifndef::rootdir[:rootdir: {docdir}/..] -:description: Configuration of io.helidon.microprofile.openapi.MPOpenAPISupport -:keywords: helidon, config, io.helidon.microprofile.openapi.MPOpenAPISupport -:basic-table-intro: The table below lists the configuration keys that configure io.helidon.microprofile.openapi.MPOpenAPISupport -include::{rootdir}/includes/attributes.adoc[] - -= MPOpenAPISupport (microprofile.openapi) Configuration - -// tag::config[] - - -Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io/helidon/microprofile/openapi/MPOpenAPISupport.html[io.helidon.microprofile.openapi.MPOpenAPISupport] - - -[source,text] -.Config key ----- -mp.openapi ----- - - - -== Configuration options - - - -Optional configuration options: -[cols="3,3,2,5a"] - -|=== -|key |type |default value |description - -|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. -|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. -|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. -|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. -|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. -|`scan.classes` |string[] |{nbsp} |Specify the list of classes to scan. -|`scan.disable` |boolean |`false` |Disable annotation scanning. -|`scan.exclude.classes` |string[] |{nbsp} |Specify the list of classes to exclude from scans. -|`scan.exclude.packages` |string[] |{nbsp} |Specify the list of packages to exclude from scans. -|`scan.packages` |string[] |{nbsp} |Specify the list of packages to scan. -|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. -|`servers` |string[] |{nbsp} |Sets servers. -|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. -|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. -|`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. - -|=== - -// end::config[] \ No newline at end of file diff --git a/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc b/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc deleted file mode 100644 index 670867d10c7..00000000000 --- a/docs/config/io.helidon.openapi.internal.OpenAPIConfigImpl.Builder.adoc +++ /dev/null @@ -1,56 +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. - -/////////////////////////////////////////////////////////////////////////////// - -ifndef::rootdir[:rootdir: {docdir}/..] -:description: Configuration of io.smallrye.openapi.api.OpenApiConfig -:keywords: helidon, config, io.smallrye.openapi.api.OpenApiConfig -:basic-table-intro: The table below lists the configuration keys that configure io.smallrye.openapi.api.OpenApiConfig -include::{rootdir}/includes/attributes.adoc[] - -= io.smallrye.openapi.api.OpenApiConfig Configuration - -// tag::config[] - - -Type: io.smallrye.openapi.api.OpenApiConfig - - - - -== Configuration options - - - -Optional configuration options: -[cols="3,3,2,5a"] - -|=== -|key |type |default value |description - -|`application-path-disable` |boolean |`false` |Sets whether the app path search should be disabled. -|`custom-schema-registry-class` |string |{nbsp} |Sets the custom schema registry class. -|`filter` |string |{nbsp} |Sets the developer-provided OpenAPI filter class name. -|`model.reader` |string |{nbsp} |Sets the developer-provided OpenAPI model reader class name. -|`schema.*` |string |{nbsp} |Sets the schema for the indicated fully-qualified class name (represented here by '*'); value is the schema in JSON format. Repeat for multiple classes. -|`servers` |string[] |{nbsp} |Sets servers. -|`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. -|`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. - -|=== - -// end::config[] \ No newline at end of file diff --git a/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc b/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc deleted file mode 100644 index 5e3648fa503..00000000000 --- a/docs/config/io.helidon.tracing.jaeger.JaegerTracerBuilder.adoc +++ /dev/null @@ -1,67 +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. - -/////////////////////////////////////////////////////////////////////////////// - -ifndef::rootdir[:rootdir: {docdir}/..] -:description: Configuration of io.helidon.tracing.Tracer -:keywords: helidon, config, io.helidon.tracing.Tracer -:basic-table-intro: The table below lists the configuration keys that configure io.helidon.tracing.Tracer -include::{rootdir}/includes/attributes.adoc[] - -= Tracer (tracing) Configuration - -// tag::config[] - -Jaeger tracer configuration. - - -Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/Tracer.html[io.helidon.tracing.Tracer] - - -This is a standalone configuration type, prefix from configuration root: `tracing` - - - -== Configuration options - - - -Optional configuration options: -[cols="3,3,2,5a"] - -|=== -|key |type |default value |description - -|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. -|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should - use a no-op tracer. -|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. -|`host` |string |{nbsp} |Host to use to connect to tracing collector. - Default is defined by each tracing integration. -|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. -|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. - Default is defined by each tracing integration. -|`port` |int |{nbsp} |Port to use to connect to tracing collector. - Default is defined by each tracing integration. -|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. - Default is defined by each tracing integration. -|`service` |string |{nbsp} |Service name of the traced service. -|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. - -|=== - -// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc b/docs/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc new file mode 100644 index 00000000000..963fac8be9e --- /dev/null +++ b/docs/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.integrations.micrometer.MicrometerSupport +:keywords: helidon, config, io.helidon.integrations.micrometer.MicrometerSupport +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.integrations.micrometer.MicrometerSupport +include::{rootdir}/includes/attributes.adoc[] + += MicrometerSupport (integrations.micrometer) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.integrations.micrometer/io/helidon/integrations/micrometer/MicrometerSupport.html[io.helidon.integrations.micrometer.MicrometerSupport] + + +[source,text] +.Config key +---- +micrometer +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`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/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc b/docs/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc new file mode 100644 index 00000000000..dac8e01d5c2 --- /dev/null +++ b/docs/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.serviceapi.MetricsSupport +:keywords: helidon, config, io.helidon.metrics.serviceapi.MetricsSupport +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.serviceapi.MetricsSupport +include::{rootdir}/includes/attributes.adoc[] + += MetricsSupport (metrics.serviceapi) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.serviceapi/io/helidon/metrics/serviceapi/MetricsSupport.html[io.helidon.metrics.serviceapi.MetricsSupport] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`appName` |string |{nbsp} |Sets the value for the `_app` tag to be applied to all metrics. +|`base` |xref:{rootdir}/config/io_helidon_metrics_api_BaseMetricsSettings.adoc[BaseMetricsSettings] |{nbsp} |Set the base metrics settings. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`enabled` |boolean |{nbsp} |Sets whether metrics should be enabled. +|`key-performance-indicators` |xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings] |{nbsp} |Set the KPI metrics settings. +|`registries` |xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[Map<string, RegistrySettings>] |{nbsp} |Sets the registry settings for the specified registry type. +|`routing` |string |{nbsp} |Sets the routing name to use for setting up the service's endpoint. +|`tags` |Map<string, string> |{nbsp} |Sets the global tags to be applied to all metrics. +|`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/includes/attributes.adoc b/docs/includes/attributes.adoc index 80c273ac2c4..4a6cc44808f 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -97,6 +97,7 @@ endif::[] :microprofile-metrics-base-url: {microprofile-base-url}/microprofile-metrics-{version-lib-microprofile-metrics-api} :microprofile-metrics-spec-url: {microprofile-metrics-base-url}/microprofile-metrics-spec-{version-lib-microprofile-metrics-api}.html :microprofile-metrics-javadoc-url: {microprofile-metrics-base-url}/apidocs +:microprofile-metrics-javadoc-annotation-url: {microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/annotation :microprofile-tracing-base-url: {microprofile-base-url}/microprofile-tracing-{version-lib-microprofile-tracing} :microprofile-tracing-spec-url: {microprofile-tracing-base-url}/microprofile-tracing-spec-{version-lib-microprofile-tracing}.html @@ -183,6 +184,7 @@ endif::[] :media-jsonb-javadoc-base-url: {javadoc-base-url}/io.helidon.media.jsonb :media-jackson-javadoc-base-url: {javadoc-base-url}/io.helidon.media.jackson :metrics-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.api +:metrics-mp-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.metrics :metrics-serviceapi-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.serviceapi :micrometer-javadoc-base-url: {javadoc-base-url}/io.helidon.integrations.micrometer :mp-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.cors @@ -217,5 +219,9 @@ endif::[] :oci-javasdk-objstore-javadoc-base-url: {oci-javasdk-javadoc-base-url}/com/oracle/bmc/objectstorage :oci-javasdk-vault-javadoc-base-url: {oci-javasdk-javadoc-base-url}/com/oracle/bmc/vault +:openmetrics-exemplar-spec-url: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars + +:micrometer-url: https://micrometer.io +:micrometer-api-url: https://micrometer.io/docs/concepts endif::attributes-included[] diff --git a/docs/includes/guides/metrics.adoc b/docs/includes/guides/metrics.adoc index 7e3d695d39f..68123bd582d 100644 --- a/docs/includes/guides/metrics.adoc +++ b/docs/includes/guides/metrics.adoc @@ -510,7 +510,7 @@ NOTE: Leave the application running in Kubernetes since it will be used for Prom ==== Prometheus Integration The metrics service that you just deployed into Kubernetes is already annotated with `prometheus.io/scrape:`. This will allow -Prometheus to discover the service and scrape the metrics. In this exercise, you will install Prometheus +Prometheus to discover the service and scrape the metrics. This example shows how to install Prometheus into Kubernetes, then verify that it discovered the Helidon metrics in your application. [source,bash] diff --git a/docs/includes/metrics/metrics-capable-components.adoc b/docs/includes/metrics/metrics-capable-components.adoc index c0222ce09c5..f7acb77356b 100644 --- a/docs/includes/metrics/metrics-capable-components.adoc +++ b/docs/includes/metrics/metrics-capable-components.adoc @@ -21,13 +21,19 @@ ifndef::flavor-lc[:flavor-lc: se] ifndef::flavor-uc[:flavor-lc: SE] ifndef::se-flavor[:se-flavor: true] ifndef::h1-prefix[:h1-prefix: SE] - -= Metrics-Capable Components +:feature-name: metrics-capable components :intro-project-name: {h1-prefix} :chk: icon:check[] :x: icon:times[] // tag::preamble[] +== Contents + +- <> +- <> +- <> + +== Overview This document explains Helidon {h1-prefix} metrics-capable components and applications and describes how to create and control them. // end::preamble[] @@ -35,11 +41,17 @@ This document explains Helidon {h1-prefix} metrics-capable components and applic == Introduction Think of Helidon metrics in three related but different parts: -* The Helidon metrics API allows your code to register, look-up, remove, and update metrics using +* The Helidon metrics API. ++ +The API allows your code to register, look-up, remove, and update metrics using the `RegistryFactory`, `MetricRegistry`, and individual metrics interfaces. -* Helidon provides two implementations of the Helidon metrics API and selects which one to use at runtime, +* Implementations of the Helidon metrics API. ++ +Helidon provides two and selects which one to use at runtime, based on what components are present on the runtime path and whether metrics is configured to be enabled or disabled. -* The built-in Helidon metrics web service supports the `/metrics` endpoints by which clients can retrieve metadata and +* The built-in Helidon metrics web service. ++ +This service supports the `/metrics` endpoint by which clients can retrieve metadata and values of the registered metrics. ifeval::["{h1-prefix}" == "MP"] Helidon MP apps which use metrics enable the metrics service by default. @@ -55,10 +67,12 @@ endif::[] As you plan and write Helidon components and applications, you make some choices about exactly how your code will use metrics. -This guide gives some background information, +This document gives some background information, describes the choices you face, explains their ramifications, and provides some code examples. -== Categorizing Metrics Usage +== Usage + +=== Categorizing Metrics Usage We can place each Helidon component and Helidon application into one of three categories based on how it relies on metrics. The type of module dictates the compile-time dependency you declare in the project `pom.xml`. @@ -82,7 +96,7 @@ The type of module dictates the compile-time dependency you declare in the proje Whenever possible, if your component or app uses metrics write it as metrics-capable code. -== Understanding the Two Metrics Implementations +=== Understanding the Two Metrics Implementations Helidon provides two metrics implementations. * _Full-featured_ metrics allows registering, removing, and updating metrics and observing metrics' changing values. @@ -104,7 +118,7 @@ For Helidon to use the full implementation, two conditions must hold: Otherwise, provided that the runtime path includes `helidon-metrics-api`, Helidon activates the minimal implementation. -== Understanding the Two Metrics Service Implementations +=== Understanding the Two Metrics Service Implementations Helidon includes two implementations of support for the metrics web service endpoint `/metrics` (or whatever context value is configured). @@ -119,7 +133,7 @@ Any code compiled with `helidon-metrics-service-api` can assume that the runtime Helidon activates the full implementation if the runtime path includes the full implementation _and_ metrics is configured as enabled; Helidon uses the minimal implementation otherwise. -== Enabling and Disabling Metrics +=== Enabling and Disabling Metrics Using ifeval::["{h1-prefix}" == "SE"] either builder-style settings or @@ -136,14 +150,14 @@ provided they are in the runtime path. Further, users can set `component-name.metrics.enabled` to `false` which disables metrics for just that component so long as the component was written to check that setting and act on it accordingly. -== Designing and Writing Metrics-capable Applications and Components +=== Designing and Writing Metrics-capable Applications and Components Whoever packages and deploys your application or component can control what code will be on the runtime path and whether metrics is enabled or not. As a result, wherever possible, construct your modules which use metrics so that they do not make decisions based on the values of metrics; that is, design them to be metrics-capable, _not_ metrics-dependent. Doing so allows your code to operate regardless of whether the full-featured metrics implementation is active at runtime. -=== Declaring Dependencies +==== Declaring Dependencies . Include this dependency: + [source,xml] @@ -188,10 +202,15 @@ implementation: // tag::writing-code-beginning[] -=== Writing the Metrics-capable Code +// tag::writing-code-intro[] +=== Writing Metrics-capable Code The way you write a metrics-capable module depends on whether it is a _component_ (that is, _not_ an application) or an _application_. +// end::writing-code-intro[] + +// tag::writing-component[] + ==== Writing a _Non-application Component_ Write your _non-application_ component to accept component-specific configuration that includes an optional `metrics` section which can include an optional `enabled` setting. Helidon defaults the value to `true`. @@ -256,12 +275,15 @@ Helidon returns either a full-featured `RegistryFactory` or a minimal one, depen * whether metrics overall is enabled or disabled, and * whether the component metrics settings requests enabled or disabled metrics. +// end::writing-component[] + // end::writing-code-beginning[] // tag::writing-code-ending[] -==== An Example: Docker Images +== Examples +=== An Example: Docker Images Here is an example showing how useful metrics-capable code can be. You (or others) could assemble a Docker image with your metrics-capable app as its top layer or your metrics-capable component in a middle layer, built on a lower layer containing several Helidon modules including the full metrics implementation. @@ -275,7 +297,9 @@ which all use a single version of your metrics-capable app or component which ru // end::writing-code-ending[] // tag::wrap-up[] -== Advantages of Writing Metrics-capable Modules +== Additional Information + +=== Advantages of Writing Metrics-capable Modules By writing a metrics-capable app or component, you give packagers and deployers of your code the flexibility to include or exclude the full metrics implementation at runtime as they see fit. diff --git a/docs/includes/metrics/metrics-config.adoc b/docs/includes/metrics/metrics-config.adoc new file mode 100644 index 00000000000..ec900a18ce3 --- /dev/null +++ b/docs/includes/metrics/metrics-config.adoc @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/../..] +ifndef::flavor-lc[:flavor-lc: se] +:description: Helidon metrics +:keywords: helidon, metrics, configuration + + +// tag::config-intro[] +== Configuration + +To control how the Helidon metrics subsystem behaves, add a `metrics` section to +ifdef::mp-flavor[your `META-INF/microprofile-config.properties` file.] +ifdef::se-flavor[your configuration file, such as `application.yaml`.] + +include::{rootdir}/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc[tag=config,leveloffset=+1] + +// end::config-intro[] + +// tag::config-examples[] +[#example-configuration] +=== Example Configuration + +Metrics configuration is quite extensive and powerful and, therefore, a bit complicated. +The rest of this section illustrates some of the most common scenarios: + +* <> +* <> +* <> +* <> + +[[config-disable]] +==== Disable Metrics Subsystem + +.Disabling metrics entirely +ifdef::mp-flavor[] +[source,properties] +---- +metrics.enabled=false +---- +endif::[] +ifdef::se-flavor[] +[source,yaml] +---- +metrics: + enabled: false +---- +endif::[] +Helidon does not update metrics, and the `/metrics` endpoints respond with `404` plus a message that the metrics subsystem is disabled. + +[[config-selective]] +==== Disable Selected Metrics + +You can be even more selective. Within a registry type you can configure up to two regular expression patterns: + +* one matching metric names to _exclude_, and +* one matching metric names to _include_. + +Helidon updates and reports a metric only if two conditions hold: + +* the metric name _does not_ match the `exclude` regex pattern (if you define one), and +* either +** there is no `include` regex pattern, or +** the metric name matches the `include` pattern. + +[CAUTION] +==== +Make sure any `include` regex pattern you specify matches _all_ the metric names you want to capture. +==== +Suppose your application creates and updates a group of metrics with names such as `myapp.xxx.queries`, `myapp.xxx.creates`, `myapp.xxx.updates`, and `myapp.xxx.deletes` where `xxx` can be either `supplier` or `customer`. + +The following example gathers all metrics _except_ those from your application regarding suppliers _although_ supplier updates are _included_: + +.Disabling and enabling metrics by name +ifdef::mp-flavor[] +[source,properties] +---- +metrics.registries.0.type=application +metrics.registries.0.application.filter.exclude=myapp\.supplier\..* +metrics.registries.0.application.filter.include=myapp\.supplier\.updates +---- +endif::[] +ifdef::se-flavor[] +[source,yaml] +---- +metrics: + registries: + - type: application + filter: + exclude: "myapp\.supplier\..*" + include: "myapp\.supplier\.updates" +---- +endif::[] + +This setting excludes metrics with names starting with `myapp.supplier` _except_ for the metric `myapp.supplier.updates`. The `exclude` and `include` values are regular expressions. + +[[config-kpi]] +==== Collecting Basic and Extended Key Performance Indicator (KPI) Metrics + +Any time you include the Helidon metrics module in your application, Helidon tracks two basic performance indicator metrics: + +* a `Counter` of all requests received (`requests.count`), and +* a `Meter` of all requests received (`requests.meter`). + +Helidon {h1-prefix} also includes additional, extended KPI metrics which are disabled by default: + +* current number of requests in-flight - a `ConcurrentGauge` (`requests.inFlight`) of requests currently being processed +* long-running requests - a `Meter` (`requests.longRunning`) measuring the rate at which Helidon processes requests which take at least a given amount of time to complete; configurable, defaults to 10000 milliseconds (10 seconds) +* load - a `Meter` (`requests.load`) measuring the rate at which requests are worked on (as opposed to received) +* deferred - a `Meter` (`requests.deferred`) measuring the rate at which a request's processing is delayed after Helidon receives the request + +You can enable and control these metrics using configuration: + +.Controlling extended KPI metrics +ifdef::mp-flavor[] +[source,properties] +---- +metrics.key-performance-indicators.extended = true +metrics.key-performance-indicators.long-running.threshold-ms = 2000 +---- +endif::[] +ifdef::se-flavor[] +[source,yaml] +---- +metrics: + key-performance-indicators: + extended: true + long-running: + threshold-ms: 2000 +---- +endif::[] + +[[config-rest-request]] +==== Enable `REST.request` metrics + +.Controlling REST request metrics +ifdef::mp-flavor[] +[source,properties] +---- +metrics.rest-request-enabled=true +---- +endif::[] +ifdef::se-flavor[] +[source,yaml] +---- +metrics: + rest-request-enabled: true +---- +endif::[] +Helidon will automatically register and update `SimpleTimer` metrics for every REST endpoint in your service. + +// end::config-examples[] \ No newline at end of file diff --git a/docs/includes/metrics/micrometer-shared.adoc b/docs/includes/metrics/micrometer-shared.adoc index 0520358df2b..6dc5cdf488d 100644 --- a/docs/includes/metrics/micrometer-shared.adoc +++ b/docs/includes/metrics/micrometer-shared.adoc @@ -14,7 +14,7 @@ limitations under the License. /////////////////////////////////////////////////////////////////////////////// - +// tag::intro[] ifndef::rootdir[:rootdir: {docdir}/../..] ifndef::flavor-lc[:flavor-lc: se] ifeval::["{flavor-lc}" == "se"] @@ -23,13 +23,12 @@ endif::[] ifeval::["{flavor-lc}" == "mp"] :mp-flavor: endif::[] +:feature-name: Micrometer support //Contains content that is shared between multiple Micrometer pages. :keywords: helidon, java, micrometer, integration, se, mp -= Micrometer Shared content - -// tag::intro[] +== Overview Helidon {h1-prefix} simplifies how you can use Micrometer for application-specific metrics: * The endpoint `/micrometer`: A configurable endpoint that exposes metrics according to which Micrometer meter registry @@ -51,9 +50,7 @@ In Helidon {helidon-version}, Micrometer support is separate from the Helidon {h // end::intro[] // tag::prereq[] -== Prerequisites - -Declare the following dependency in your project: +include::{rootdir}/includes/dependencies.adoc[] [source,xml,subs="verbatim,attributes"] ---- @@ -79,7 +76,7 @@ configuration to set them up as you wish. // end::prereq[] // tag::overriding-intro[] -=== Overriding Defaults for Built-in Meter Registry Types +==== Overriding Defaults for Built-in Meter Registry Types Unless you specify otherwise, Helidon uses defaults for any built-in Micrometer meter registry. For example, Helidon configures the built-in Prometheus registry using `PrometheusConfig.DEFAULT`. @@ -90,22 +87,39 @@ For example, Helidon configures the built-in Prometheus registry using `Promethe To use configuration to control the selection and behavior of Helidon's built-in Micrometer meter registries, include in your configuration (such as `application.yaml`) a `micrometer.builtin-registries` section. -[source,yaml] .Enroll Prometheus built-in meter registry using default configuration +ifdef::se-flavor[] +[source,yaml] ---- micrometer: builtin-registries: - type: prometheus ---- +endif::[] +ifdef::mp-flavor[] +[source,properties] +---- +micrometer.builtin-registries.0.type=prometheus +---- +endif::[] -[source,yaml] .Enroll Prometheus built-in meter registry with non-default configuration +ifdef::se-flavor[] +[source,yaml] ---- micrometer: builtin-registries: - type: prometheus prefix: myPrefix ---- +endif::[] +ifdef::mp-flavor[] +[source,properties] +---- +micrometer.builtin-registries.0.type=prometheus +micrometer.builtin-registries.0.prefix=myPrefix +---- +endif::[] Note that the first config example is equivalent to the default Helidon Micrometer behavior; Helidon by default supports the Prometheus meter registry. The configuration keys that are valid for the `builtin-registries` child entries depend on the type of Micrometer meter @@ -120,17 +134,54 @@ meter registries. // end::overriding-using-config[] // tag::accessing-endpoint-intro[] -== Accessing the Helidon Micrometer Endpoint -By default, Helidon Micrometer integration exposes the `/micrometer` endpoint. You can override this -using +=== Accessing the Helidon Micrometer Endpoint +Helidon automatically creates a REST endpoint which clients can access to retrieve Micrometer metrics, by default at the `/micrometer` endpoint. + ifdef::se-flavor[] -the `Builder` or +Within Helidon, each type of meter registry is paired with code that examines the incoming HTTP request and decides +whether the request matches up with the associated meter registry. The first pairing that accepts the request +returns the response. +endif::[] +// end::accessing-endpoint-intro[] + +// tag::configuring-endpoint[] +== Configuration +You can configure the Helidon Micrometer REST service as you can other built-in Helidon services by adding configuration settings under the `micrometer` top-level key. + +include::{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[leveloffset=+1] + +By default, Helidon Micrometer integration exposes the `/micrometer` endpoint. +You can override the path using +ifdef::se-flavor[] +the link:{micrometer-javadoc-base-url}/MicrometerSupport.Builder.html[`Builder`] or endif::se-flavor[] the `micrometer.web-context` configuration key. -// end::accessing-endpoint-intro[] +.Overriding the default Micrometer path +ifdef::se-flavor[] +[source,yaml] +---- +micrometer: + web-context: my-micrometer +---- +endif::[] +ifdef::mp-flavor[] +[source,properties] +---- +micrometer.web-context=my-micrometer +---- +endif::[] + +// end::configuring-endpoint[] // tag::accessing-endpoint-end[] // end::accessing-endpoint-end[] +// Following tag assumes the referring document is mid-sentence, with something like "Your code" +// tag::use-micrometer-api[] +can create, look up, and update metrics programmatically using the Micrometer `MeterRegistry` API. The link:{micrometer-api-url}[Micrometer concepts document] provides a good starting point for learning how to use Micrometer's interfaces and classes. +// end::use-micrometer-api[] + +// end::all-micrometer[] + diff --git a/docs/includes/metrics/prometheus-exemplar-support.adoc b/docs/includes/metrics/prometheus-exemplar-support.adoc index b6b1c8cf27a..b38787ca994 100644 --- a/docs/includes/metrics/prometheus-exemplar-support.adoc +++ b/docs/includes/metrics/prometheus-exemplar-support.adoc @@ -21,12 +21,35 @@ ifndef::flavor-lc[:flavor-lc: se] :description: Helidon metrics :keywords: helidon, metrics, exemplar, prometheus, OpenMetrics +:feature-name: OpenMetrics exemplar support -Add Helidon {h1-prefix} support for link:https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars[OpenMetrics (Prometheus) exemplars] for histograms, counters, and simple timers to your application simply by adding dependencies to your project's `pom.xml`. +== Contents -== Prerequisites +- <> +- <> +- <> +- <> +- <> -Declare the following dependency in your project: +== Overview + +A metric typically reflects the usage of a _single_ point in your service processing _multiple_ requests over time. +A value such as the total time consumed by a given REST endpoint underscores the aggregate nature of metric values; Helidon accumulates the time from all requests in the total duration. + +Tracing, on the other hand, captures the usage of _multiple_ parts of your code as your service responds to a _single_ request. + +Metrics and tracing come together in Helidon's support for examplars. + +[NOTE] +-- +link:https://www.merriam-webster.com/dictionary/exemplar[_exemplar_] - one that serves as a model or example +[.text-right] +-- Merriam-Webster Dictionary +-- + +In the context of metrics, an _exemplar_ for a given metric is a specific trace which, in some sense, made a typical contribution to the metric's value. For example, an exemplar for a `SimpleTimer` might be a trace in which the duration it contributed to the value of a `SimpleTimer` is near the mean of the durations over all traces. + +include::{rootdir}/includes/dependencies.adoc[] [source,xml,subs="verbatim,attributes"] ---- @@ -37,67 +60,100 @@ Declare the following dependency in your project: ---- +Also, include the Helidon integration module for a tracing implementation...for example ifdef::se-flavor[] -Also, include either xref:{rootdir}/se/tracing/zipkin.adoc[Helidon Zipkin] or -xref:{rootdir}/se/tracing/jaeger.adoc[Helidon Jaeger] support: +xref:{rootdir}/se/tracing/zipkin.adoc[Helidon Zipkin] endif::[] ifdef::mp-flavor[] -Also, include either xref:{rootdir}/mp/tracing.adoc#zipkin-tracing[Helidon Zipkin] or -xref:{rootdir}/mp/tracing.adoc#jaeger-tracing[Helidon Jaeger] support: +xref:{rootdir}/mp/tracing.adoc#zipkin-tracing[Helidon Zipkin]: endif::[] - +support: include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-dependency] -or -include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-dependency] -Be sure Zipkin or Jaeger, whichever you chose, is running and accessible to your server. +Add the Helidon tracing component itself: +ifdef::se-flavor[] +include::{rootdir}/se/tracing/tracing.adoc[tag=tracing-dependency] +endif::[] +ifdef::mp-flavor[] +include::{rootdir}/mp/tracing.adoc[tag=tracing-dependency] +endif::[] -== Interpreting Exemplars +== Usage +Once you add the appropriate dependencies to your project, exemplar support runs automatically as part of Helidon metrics. You do not need to change your application or configuration. -[NOTE] --- -link:https://www.merriam-webster.com/dictionary/exemplar[_exemplar_] - one that serves as a model or example -[.text-right] --- Merriam-Webster Dictionary --- +=== Interpreting Exemplars + +Helidon automatically records a sample (label, value, and timestamp) with each update to a histogram, simple timer, or counter. When a client accesses the `/metrics` endpoint, Helidon adds the label, value, and timestamp to the OpenMetrics response. + +Helidon adds an exemplar to the output for each statistical value--such as minimum, maximum, mean, and quantiles--for histograms, timers, simple times, and for counters. The exemplar information in the output describes a single, actual sample that is representative of the statistical value. +Helidon chooses the representative examplar for each value using information that is already recorded for each type of metric: + +.Selection of exemplars for types of metrics +[%autowidth] +|==== +|Metric Type| Example | Sample Selected as Exemplar + +| corresponds directly to a specific sample +| minimum or maximum of a value +| any sample with that exact value + +| collecs samples into bins (quantiles) +| histogram (as with timers) +| any sample from the bin + +| maintains running statistics +| counts, totals +| most recent sample + +| computes its value from multiple samples +| mean +| sample for which its value is at least as close as other samples to the statistical calculation +|==== + +In cases with multiple representative samples (for example, two samples' values are equally close to the mean), Helidon chooses one of them arbitrarily. -When you add the `helidon-metrics-trace-exemplar` dependency--and one for either Zipkin or Jaeger--to your application, Helidon automatically records a sample (label, value, and timestamp) with each update to a histogram, simple timer, or counter. Helidon adds the label, value, and timestamp to the OpenMetrics output returned from the Helidon metrics endpoint (`/metrics` unless you set it up otherwise). +=== Output Format +In the OpenMetrics output, an exemplar actually appears as a comment appended to the normal OpenMetrics output. + +.OpenMetrics format with exemplars +[source,subs="quotes"] +---- +_metric-identifier_ _metric-value_ # _exemplar-label_ _sample-timestamp_ +---- +Even downstream consumers of OpenMetrics output that do not recognize the exemplar format should continue to work correctly (as long as they _do_ recognize comments). + +But some consumers, such as trace collectors and their U/Is, understand the exemplar format, and they allow you to browse metrics and then navigate directly to the trace for the metric's exemplar. + +== Examples .Exemplar output - `Timer` -[listing] +[listing,subs="quotes"] ---- # TYPE application_getTimer_mean_seconds gauge -application_getTimer_mean_seconds 8.303030623354298E-4 # {trace_id="067632454fe4e8d1"} 1.14701E-4 1617723032.570000 <1> +application_getTimer_mean_seconds 8.303030623354298E-4 *# {trace_id="067632454fe4e8d1"} 1.14701E-4 1617723032.570000* # TYPE application_getTimer_max_seconds gauge -application_getTimer_max_seconds 0.003952636 # {trace_id="fce183094e471633"} 0.003952636 1617723030.108000 <2> +application_getTimer_max_seconds 0.003952636 *# {trace_id="fce183094e471633"} 0.003952636 1617723030.108000* # TYPE application_getTimer_min_seconds gauge -application_getTimer_min_seconds 5.5254E-5 # {trace_id="0b1a4bf22b4e47fd"} 5.5254E-5 1617723033.311000 +application_getTimer_min_seconds 5.5254E-5 *# {trace_id="0b1a4bf22b4e47fd"} 5.5254E-5 1617723033.311000* ---- -<1> This exemplar is a sample with value at least as close to the mean as any other sample. -<2> This exemplar is for an exact sample with value the same as the maximum value the timer has observed. +The first exemplar is a sample with value at least as close to the mean for that timer as any other sample. + +This second exemplar is for an exact sample with value the same as the maximum value the timer has observed. .Exemplar output - `SimpleTimer` -[listing] +[listing,subs="quotes"] ---- # TYPE application_globalRequestTracker_total counter # HELP application_globalRequestTracker_total -application_globalRequestTracker_total 4 # {trace_id="daf26fe35fee9917"} 0.001183992 1617725180.234000 <1> +application_globalRequestTracker_total 4 *# {trace_id="daf26fe35fee9917"} 0.001183992 1617725180.234000* # TYPE application_globalRequestTracker_elapsedTime_seconds gauge -application_globalRequestTracker_elapsedTime_seconds 0.030309068 # {trace_id="daf26fe35fee9917"} 0.001183992 1617725180.234000 <1> +application_globalRequestTracker_elapsedTime_seconds 0.030309068 *# {trace_id="daf26fe35fee9917"} 0.001183992 1617725180.234000* ---- -<1> The exemplar for a `SimpleTimer` is the same for the `total` and the `elapsedTime` sub metrics: always the most recent sample which updated the `SimpleTimer`. - -Helidon adds an exemplar to the output for each statistical value--such as minimum, maximum, mean, and quantiles--for histograms, timers, simple times, and for counters. The exemplar information describes a single, actual sample that is representative of the statistical value. -Helidon chooses the representative examplar for each value using information that is already recorded for each type of metric: - -. If a metric necessarily corresponds to a specific sample--for example a minimum or maximum--Helidon associates a sample that has that exact value as the exemplar for the metric. -. If a metric collects samples into bins (quantiles), Helidon associates a sample from that bin with the bin's output. -. If a metric maintains running statistics (counts, totals), Helidon associates the most recent sample for that metric. -. If Helidon computes a metric's value from a number of samples--for example, mean--Helidon associates a sample for which its value is at least as close as other samples to the statistical calculation. - -In cases with multiple representative samples (for example, two samples' values are equally close to the mean), Helidon chooses one of them arbitrarily. +The exemplar for a `SimpleTimer` is the same for the `total` and the `elapsedTime` sub metrics: always the most recent sample which updated the `SimpleTimer`. +== Additional Information +Brief discussion of link:{openmetrics-exemplar-spec-url}[exemplars in the OpenMetrics spec] \ No newline at end of file diff --git a/docs/includes/pages.adoc b/docs/includes/pages.adoc index 093c2ffbc63..b31b6acb5b1 100644 --- a/docs/includes/pages.adoc +++ b/docs/includes/pages.adoc @@ -22,6 +22,6 @@ ifdef::se-flavor[] endif::[] ifdef::mp-flavor[] :health-page: {rootdir}/mp/health.adoc -:metrics-page: {rootdir}/mp/metrics/introduction.adoc +:metrics-page: {rootdir}/mp/metrics/metrics.adoc :openapi-page: {rootdir}/mp/openapi.adoc endif::[] \ No newline at end of file diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index 2e47e64fbb5..37340fa0f01 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -151,7 +151,7 @@ Distributed transactions for microservices following SAGA pattern. //Metrics [CARD] .Metrics -[icon=av_timer,link=../metrics/introduction.adoc] +[icon=av_timer,link=../metrics/metrics.adoc] -- Instrumentation to expose metrics of your applications. -- diff --git a/docs/mp/metrics/introduction.adoc b/docs/mp/metrics/introduction.adoc deleted file mode 100644 index 3a5827179d0..00000000000 --- a/docs/mp/metrics/introduction.adoc +++ /dev/null @@ -1,49 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Metrics -:toc: -:toc-placement: preamble -:description: MicroProfile Metrics support in Helidon MP -:keywords: helidon, mp, microprofile, metrics -:feature-name: MicroProfile Metrics -:microprofile-bundle: true -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.microprofile.metrics - helidon-microprofile-metrics - ----- - -== Overview - -Helidon provides three types of metrics: base, vendor, and application. Helidon automatically provides built-in base and vendor metrics. -Applications can use these metrics without additional configuration or code changes. - -== Next Steps - -Learn more about link:{microprofile-metrics-spec-url}[MicroProfile Metrics specification]. - -Create a sample MicroProfile (MP) project that can be used to run some basic examples using both built-in and custom metrics with Helidon MP. xref:../guides/metrics.adoc[Helidon MP Metrics Guide]. \ No newline at end of file diff --git a/docs/mp/metrics/metrics-capable-components.adoc b/docs/mp/metrics/metrics-capable-components.adoc index 8527fa39826..b05e66b2309 100644 --- a/docs/mp/metrics/metrics-capable-components.adoc +++ b/docs/mp/metrics/metrics-capable-components.adoc @@ -17,7 +17,7 @@ /////////////////////////////////////////////////////////////////////////////// = Metrics-Capable Modules -:description: Helidon MP metrics-capable components +:description: Helidon MP metrics-capable modules :keywords: helidon, metrics, metrics-capable, microprofile, guide :intro-project-name: {h1-prefix} :chk: icon:check[] @@ -26,9 +26,10 @@ include::{rootdir}/includes/mp.adoc[] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=preamble] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=all-beginning-text] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=writing-code-beginning] +// Overview and usage +include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=preamble;all-beginning-text] + +include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=writing-code-intro] ==== Writing a Helidon MP Application @@ -66,6 +67,8 @@ That said, a developer of a Helidon MP app _can_ explicitly exclude the dependen In the resulting Helidon MP application, Helidon will use the minimal metrics and metrics support implementations. +include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tag=writing-component] + include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=writing-code-ending] include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=wrap-up] \ No newline at end of file diff --git a/docs/mp/metrics/metrics.adoc b/docs/mp/metrics/metrics.adoc new file mode 100644 index 00000000000..30a7b317c3a --- /dev/null +++ b/docs/mp/metrics/metrics.adoc @@ -0,0 +1,634 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += Metrics in Helidon MP +:toc: +:toclevels: 2 +:toc-placement: preamble +:description: Helidon MP Metrics Support +:keywords: helidon, java, metrics, mp, configuration, services +:feature-name: metrics +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/mp.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview +Helidon MP metrics implements the MicroProfile Metrics specification, providing: + +* a unified way for MicroProfile servers to export monitoring data--telemetry--to management agents, and +* a unified Java API which all application programmers can use to expose telemetry data from their services. + +Learn more about the https://github.com/eclipse/microprofile-metrics/releases/tag/{version-lib-microprofile-metrics-api}[MicroProfile Metrics specification]. + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml,subs="attributes+"] +---- + + io.helidon.microprofile.metrics + helidon-microprofile-metrics + +---- + +== Usage + +=== Instrumenting your Service +You can add metrics to your service in these ways: + +* Annotate bean methods--typically your REST resource endpoint methods (the Java code that receives incoming REST requests); Helidon automatically registers these metrics and updates them when the annotated methods are invoked via CDI. +* Write code in your service which explicitly invokes the metrics API to register metrics, retrieve previously-registered metrics, and update metric values. +* Configure some simple `REST.request` metrics which Helidon automatically registers and updates for all REST resource endpoints. + +Later sections of this document describe how to do each of these. + +=== Categorizing Types of Metrics +Helidon distinguishes among three general _types_, or scopes, of metrics, as described in the MP metrics specification. + +.Types (scopes) of metrics +[%autowidth] +|==== +| Type/scope | Typical Usage + +| base | Mandated by the MP metrics specification, such as OS or Java runtime measurements (available heap, disk space, etc.). +| vendor | Implemented by vendors, including the `REST.request` metrics and other key performance indicator measurements (described in later sections). +| application | Declared via annotations or programmatically registered by your service code. +|==== + +When you add metrics annotations to your service code, Helidon registers the resulting metrics as type `application`. + +=== Metric Registries +A _metric registry_ collects registered metrics of a given type. Helidon supports three registries, one for each of the three metrics types. + +When you add code to your service to create a metric programmatically, the code first locates the appropriate registry and then the registers the metric with that registry. + +=== Retrieving Metrics Reports from your Service +When you add the metrics dependency to your project, Helidon automatically provides a built-in REST endpoint `/metrics` which responds with a report of the registered metrics and their values. + +Clients can request a particular output format. + +.Formats for `/metrics` output +[%autowidth] +|==== +| Format | Requested by + +| OpenMetrics (Prometheus) | default +| JSON | Header `Accept: application/json` +|==== + +Clients can also limit the report by appending the metric type to the path: + +* `/metrics/base` +* `/metrics/vendor` +* `/metrics/application` + +Further, clients can narrow down to a specific metric name by adding the name as a subpath such as `/metrics/application/myCount`. + +== API + +The link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/package-summary.html[MicroProfile Metrics API] prescribes all the standard interfaces related to metrics. This section summarizes a few key points about using that API and explains some Helidon-specific interfaces. + +=== Metrics Annotations +You can very easily instrument your service and refer to registered metrics by annotating methods to be measured and injecting metrics which your code needs to observe. + +==== Metric-defining Annotations +The MicroProfile Metrics specification describes several metric types you can create using annotations, summarized in the following table: + +.Metrics Annotations +[%autowidth] +|==== +| Annotation | Usage + +| link:{microprofile-metrics-javadoc-annotation-url}/Counted.html[`@Counted`] +| Monotonically increasing count of events. + +| link:{microprofile-metrics-javadoc-annotation-url}ConcurrentGauge.html[`@ConcurrentGauge`] +| Increasing and decreasing measurement of currently-executing blocks of code. + +| link:{microprofile-metrics-javadoc-annotation-url}/Gauge.html[`@Gauge`] +| Access to a value managed by other code in the service. + +| link:{microprofile-metrics-javadoc-annotation-url}/Metered.html[`@Metered`] +| Count of invocations and how frequently invocations have occurred. + +| link:{microprofile-metrics-javadoc-annotation-url}/SimplyTimed.html[`@SimplyTimed`] +| Count of invocations and the total duration consumed by those invocations. + +| link:{microprofile-metrics-javadoc-annotation-url}/Timed.html[`@Timed`] +| Frequency of invocations and the distribution of how long the invocations take. + +|==== + +Place annotations on constructors or methods to measure those specific executables. If you annotate the class instead, Helidon applies that annotation to all constructors and methods which the class declares. + +==== Metric-referencing Annotations +To get a reference to a specific metric, use a metric-referencing annotation in any bean, including your REST resource classes. + +You can `@Inject` a field of the correct type. Helidon uses the MicroProfile Metrics naming conventions to select which specific metric to inject. Use the link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/annotation/Metric.html[`@Metric`] annotation to control that selection. + +You can also add `@Metric` on a constructor or method parameter to trigger injection there. + +Helidon automatically looks up the metric referenced from any injection site and provides a reference to the metric. Your code then simply invokes methods on the injected metric. + +=== The `MetricRegistry` API +To register or look up metrics programmatically, your service code uses one of the three link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/MetricRegistry.html[`MetricRegistry`] instances (base, vendor, and application) which Helidon furnishes automatically. + +To get a `MetricRegistry` reference + +* `@Inject` the metric registry you want, perhaps also using the link:{microprofile-metrics-javadoc-annotation-url}/RegistryType.html[`@RegistryType`] annotation to select the registry type, or +* Get a Helidon link:{metrics-javadoc-base-url}/RegistryFactory.html[`RegistryFactory`]; either ++ +-- +** `@Inject` `RegistryFactory` or +** Invoke one of the static `getInstance` methods on `RegistryFactory` +-- ++ +Then invoke `getRegistry` on the `RegistryFactory` instance. + +The `MetricRegistry` allows your code to register new metrics, look-up previously-registered metrics, and remove metrics. + +// Here's Configuration. +include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro] + +== Examples + +* <> +* <> + +[#example-application-code] +=== Example Application Code + +==== Adding Method-level Annotations +The following example adds a new resource class, `GreetingCards`, to the Helidon MP QuickStart example. It shows how to use the `@Counted` annotation to track the number of times +the `/cards` endpoint is called. + +[source,java] +.Create a new class `GreetingCards` with the following code: +---- +package io.helidon.examples.quickstart.mp; + +import java.util.Collections; +import jakarta.enterprise.context.RequestScoped; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.metrics.annotation.Counted; + +@Path("/cards") //<1> +@RequestScoped // <2> +public class GreetingCards { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(name = "any-card") // <3> + public JsonObject anyCard() throws InterruptedException { + return createResponse("Here are some random cards ..."); + } + + private JsonObject createResponse(String msg) { + return JSON.createObjectBuilder().add("message", msg).build(); + } +} +---- +<1> This class is annotated with `Path` which sets the path for this resource +as `/cards`. +<2> The `@RequestScoped` annotation defines that this bean is +request scoped. The request scope is active only for the duration of +one web service invocation and it is destroyed at the end of that +invocation. +<3> The annotation `@Counted` will register a `Counter` metric for this method, creating it if needed. +The counter is incremented each time the anyCards method is called. The `name` attribute is optional. + +// tag::build-and-access-cards-example[] + +// tag::build-cards-example[] +.Build and run the application +[source,bash] +---- +mvn package +java -jar target/helidon-quickstart-mp.jar +---- +// end::build-cards-example[] + +// tag::access-cards-example[] +.Access the application endpoints +[source,base] +---- +curl http://localhost:8080/cards +curl http://localhost:8080/cards +curl -H "Accept: application/json" http://localhost:8080/metrics/application +---- +// end::access-cards-example[] + +// end::build-and-access-cards-example[] + +[source,json] +.JSON response: +---- +{ + "io.helidon.examples.quickstart.mp.GreetingCards.any-card":2 // <1> +} +---- +<1> The any-card count is two, since you invoked the endpoint twice. + +NOTE: Notice the counter name is fully qualified with the class and method names. You can remove the prefix by using the `absolute=true` field in the `@Counted` annotation. +You must use `absolute=false` (the default) for class-level annotations. + +==== Additional Method-level Metrics + +The `@ConcurrentGauge`, @Timed`, `@Metered`, and `@SimplyTimed` annotations can also be used with a method. For the following example. +you can just annotate the same method with `@Metered` and `@Timed`. These metrics collect significant +information about the measured methods, but at a cost of some overhead and more complicated output. +Use `@SimplyTimed` in cases where capturing the invocation count and the total elapsed time +spent in a block of code is sufficient. + +Note that when using multiple annotations on a method, you *must* give the metrics different names as shown below (although they do not have to be absolute). + +[source,java] +.Update the `GreetingCards` class with the following code: +---- +package io.helidon.examples.quickstart.mp; + +import java.util.Collections; +import jakarta.enterprise.context.RequestScoped; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.metrics.MetricUnits; +import org.eclipse.microprofile.metrics.annotation.Counted; +import org.eclipse.microprofile.metrics.annotation.Metered; +import org.eclipse.microprofile.metrics.annotation.Timed; + +@Path("/cards") +@RequestScoped +public class GreetingCards { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(name = "cardCount", absolute = true) //<1> + @Metered(name = "cardMeter", absolute = true, unit = MetricUnits.MILLISECONDS) //<2> + @Timed(name = "cardTimer", absolute = true, unit = MetricUnits.MILLISECONDS) //<3> + public JsonObject anyCard() { + return createResponse("Here are some random cards ..."); + } + + private JsonObject createResponse(String msg) { + return JSON.createObjectBuilder().add("message", msg).build(); + } +} + +---- +<1> Specify a custom name for the `Counter` metric and set `absolute=true` to remove the path prefix from the name. +<2> Add the `@Metered` annotation to get a `Meter` metric. +<3> Add the `@Timed` annotation to get a `Timer` metric. + +include::metrics.adoc[tag=build-and-access-cards-example] + +[source,json] +.JSON response: +---- +{ + "cardCount": 2, + "cardMeter": { // <1> + "count": 2, + "meanRate": 0.15653506570241812, + "oneMinRate": 0, + "fiveMinRate": 0, + "fifteenMinRate": 0 + }, + "cardTimer": { // <2> + "count": 2, + "elapsedTime": 2, + "meanRate": 0.15651866263362785, + "oneMinRate": 0, + "fiveMinRate": 0, + "fifteenMinRate": 0, + "min": 0, + "max": 2, + "mean": 1.0506565, + "stddev": 1.0405735, + "p50": 2.09123, + "p75": 2.09123, + "p95": 2.09123, + "p98": 2.09123, + "p99": 2.09123, + "p999": 2.09123 + } +} +---- +<1> The `Meter` metric includes the count field (it is a superset of `Counter`). +<2> The `Timer` metric includes the `Meter` fields (it is a superset of `Meter`). + + +==== Class-level Metrics + +You can collect metrics at the class-level to aggregate data from all methods in that class using the same metric. +The following example introduces a metric to count all card queries. In the following example, the method-level metrics are not +needed to aggregate the counts, but they are left in the example to demonstrate the combined output of all three metrics. + +[source,java] +.Update the `GreetingCards` class with the following code: +---- +package io.helidon.examples.quickstart.mp; + +import java.util.Collections; +import jakarta.enterprise.context.RequestScoped; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.metrics.annotation.Counted; + +@Path("/cards") +@RequestScoped +@Counted(name = "totalCards") // <1> +public class GreetingCards { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(absolute = true) // <2> + public JsonObject anyCard() throws InterruptedException { + return createResponse("Here are some random cards ..."); + } + + @Path("/birthday") + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(absolute = true) // <3> + public JsonObject birthdayCard() throws InterruptedException { + return createResponse("Here are some birthday cards ..."); + } + + private JsonObject createResponse(String msg) { + return JSON.createObjectBuilder().add("message", msg).build(); + } +} +---- +<1> This class is annotated with `@Counted`, which aggregates count data from all the method that have a `Count` annotation. +<2> Use `absolute=true` to remove path prefix for method-level annotations. +<3> Add a method with a `Counter` metric to get birthday cards. + +include::metrics.adoc[tag=build-cards-example] +[source,bash] +.Access the application endpoints +---- +curl http://localhost:8080/cards +curl http://localhost:8080/cards/birthday +curl -H "Accept: application/json" http://localhost:8080/metrics/application +---- + +[source,json] +.JSON response from `/metrics/application`: +---- +{ + "anyCard": 1, + "birthdayCard": 1, + "io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 2 // <1> +} +---- +<1> The `totalCards` count is a total of all the method-level `Counter` metrics. Class level metric names are always +fully qualified. + + +==== Field Level Metrics + +Field level metrics can be injected into managed objects, but they need to be updated by the application code. +This annotation can be used on fields of type `Meter`, `Timer`, `Counter`, and `Histogram`. + +The following example shows how to use a field-level `Counter` metric to track cache hits. + +[source,java] +.Update the `GreetingCards` class with the following code: +---- +package io.helidon.examples.quickstart.mp; + +import java.util.Collections; +import java.util.Random; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.annotation.Counted; +import org.eclipse.microprofile.metrics.annotation.Metric; + +@Path("/cards") +@RequestScoped +@Counted(name = "totalCards") +public class GreetingCards { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + @Inject + @Metric(name = "cacheHits", absolute = true) // <1> + private Counter cacheHits; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(absolute = true) + public JsonObject anyCard() throws InterruptedException { + updateStats(); // <2> + return createResponse("Here are some random cards ..."); + } + + @Path("/birthday") + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(absolute = true) + public JsonObject birthdayCard() throws InterruptedException { + updateStats(); // <3> + return createResponse("Here are some birthday cards ..."); + } + + private JsonObject createResponse(String msg) { + return JSON.createObjectBuilder().add("message", msg).build(); + } + + private void updateStats() { + if (new Random().nextInt(3) == 1) { + cacheHits.inc(); // <4> + } + } +} +---- +<1> A `Counter` metric field, `cacheHits`, is automatically injected by Helidon. +<2> Call `updateStats()` to update the cache hits. +<3> Call `updateStats()` to update the cache hits. +<4> Randomly increment the `cacheHits` counter. + +[source,bash] +.Build and run the application, then invoke the following endpoints: +---- +curl http://localhost:8080/cards +curl http://localhost:8080/cards +curl http://localhost:8080/cards/birthday +curl http://localhost:8080/cards/birthday +curl http://localhost:8080/cards/birthday +curl -H "Accept: application/json" http://localhost:8080/metrics/application +---- + +[source,json] +.JSON response from `/metrics/application`: +---- +{ + "anyCard": 2, + "birthdayCard": 3, + "cacheHits": 2, // <1> + "io.helidon.examples.quickstart.mp.totalCards.GreetingCards": 5 +} +---- +<1> The cache was hit two times out of five queries. + +==== Gauge Metric + +The metrics you have tested so far are updated in response to an application REST request, i.e GET `/cards`. These +metrics can be declared in a request scoped class and Helidon will store the metric in the `MetricRegistry`, so the value persists +across requests. When GET `/metrics/application` is invoked, Helidon will return the current value of the metric stored in the `MetricRegistry`. +The `Gauge` metric is different from all the other metrics. The application must provide a getter to return the gauge value in an +application scoped class. When GET `/metrics/application` is invoked, Helidon will call the `Gauge` getter, store that value +in the `MetricsRegistry`, and return it as part of the metrics response payload. So, the `Gauge` metric value is updated real-time, in response to the +get metrics request. + +The following example demonstrates how to use a `Gauge` to track application up-time. + +[source,java] +.Create a new `GreetingCardsAppMetrics` class with the following code: +---- +package io.helidon.examples.quickstart.mp; + +import java.time.Duration; +import java.util.concurrent.atomic.AtomicLong; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import org.eclipse.microprofile.metrics.annotation.Gauge; + +@ApplicationScoped // <1> +public class GreetingCardsAppMetrics { + + private AtomicLong startTime = new AtomicLong(0); // <2> + + public void onStartUp(@Observes @Initialized(ApplicationScoped.class) Object init) { + startTime = new AtomicLong(System.currentTimeMillis()); // <3> + } + + @Gauge(unit = "TimeSeconds") + public long appUpTimeSeconds() { + return Duration.ofMillis(System.currentTimeMillis() - startTime.get()).getSeconds(); // <4> + } +} +---- +<1> This managed object must be application scoped to properly register and use the `Gauge` metric. +<2> Declare an `AtomicLong` field to hold the start time of the application. +<3> Initialize the application start time. +<4> Return the application `appUpTimeSeconds` metric, which will be included in the application metrics. + + +[source,java] +.Update the `GreetingCards` class with the following code to simplify the metrics output: +---- +package io.helidon.examples.quickstart.mp; + +import java.util.Collections; +import jakarta.enterprise.context.RequestScoped; +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.metrics.annotation.Counted; + +@Path("/cards") +@RequestScoped +public class GreetingCards { + + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(name = "cardCount", absolute = true) + public JsonObject anyCard() throws InterruptedException { + return createResponse("Here are some random cards ..."); + } + + private JsonObject createResponse(String msg) { + return JSON.createObjectBuilder().add("message", msg).build(); + } +} +---- + +[source,bash] +.Build and run the application, then invoke the application metrics endpoint: +---- +curl -H "Accept: application/json" http://localhost:8080/metrics/application +---- + +[source,json] +.JSON response from `/metrics/application`: +---- +{ + "cardCount": 0, + "io.helidon.examples.quickstart.mp.GreetingCardsAppMetrics.appUpTimeSeconds": 6 // <1> +} +---- +<1> The application has been running for 6 seconds. + +// Config examples +include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-examples] + +== Additional Information + +include::{rootdir}/includes/guides/metrics.adoc[tag=k8s-and-prometheus-integration] + +=== References +link:{microprofile-metrics-spec-url}[MicroProfile Metrics specification] + +link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/package-info.html[MicroProfile Metrics API] diff --git a/docs/mp/metrics/micrometer.adoc b/docs/mp/metrics/micrometer.adoc index 50e14b217f0..a32f2aa87df 100644 --- a/docs/mp/metrics/micrometer.adoc +++ b/docs/mp/metrics/micrometer.adoc @@ -16,18 +16,56 @@ /////////////////////////////////////////////////////////////////////////////// -= Micrometer Metrics += Micrometer Support :description: Helidon Micrometer integration :keywords: micrometer, helidon, metrics, integration, microprofile :rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] -include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=intro] -include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=prereq] +== Contents +- <> +- <> +- <> +- <> +- <> +- <> +- <> -== Using Micrometer in Your Application -Add the Micrometer `@Timed` and `@Counted` annotations to methods in your application. +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=intro] +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=prereq] + +== Usage + +=== Registering and Updating Meters +To use Micrometer support, you can simply add the Micrometer `@Timed` and `@Counted` annotations to methods in your application. Helidon automatically registers those meters with the Micrometer composite `MeterRegistry`. + +In addition to annotating your methods, your code +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=use-micrometer-api] + + +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=accessing-endpoint-intro] + + + + +== API + +=== The Helidon Micrometer API +Helidon automatically registers and updates meters associated with methods in your service where you add the Micrometer annotations. + +If you want to use the Micrometer `MeterRegistry` directly from your own code, simply `@Inject` the `MeterRegistry` into one of your REST resource classes or any other bean which CDI recognizes. Helidon injects the same Micrometer `MeterRegistry` that it uses for handling Micrometer annotations you add to your code. + +=== The Micrometer API + +Your code +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=use-micrometer-api] + +// Configuration +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=configuring-endpoint] + +== Examples +Helidon provides an link:{helidon-github-tree-url}/examples/integrations/micrometer/mp[example MP application] using Micrometer integration. The examples below enhance the Helidon MP QuickStart application to track (by time and invocation count) all `GET` methods and to count all requests for a personalized greeting. @@ -79,8 +117,9 @@ Helidon automatically injects a reference to the `MeterRegistry` it manages into normal Micrometer API with this registry to create, find, update, and even delete meters. include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=overriding-intro;overriding-using-config] -include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=accessing-endpoint-intro] -Within Helidon, each type of meter registry is paired with code that examines the incoming HTTP request and decides - whether the request matches up with the associated meter registry. The first pairing that accepts the request - returns the response. + +== Additional Information + +The link:https://micrometer.io[Micrometer website] describes the project as a whole and has links to more information. + diff --git a/docs/mp/metrics/prometheus-exemplar-support.adoc b/docs/mp/metrics/prometheus-exemplar-support.adoc index 6611a12facf..b869b2f9e56 100644 --- a/docs/mp/metrics/prometheus-exemplar-support.adoc +++ b/docs/mp/metrics/prometheus-exemplar-support.adoc @@ -16,9 +16,9 @@ /////////////////////////////////////////////////////////////////////////////// -= Metrics Support for Exemplars += OpenMetrics Exemplar Support :description: Helidon metrics -:keywords: helidon, metrics, exemplar, prometheus, OpenMetrics +:keywords: helidon, metrics, exemplar, prometheus, OpenMetrics, tracing :rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] diff --git a/docs/mp/tracing.adoc b/docs/mp/tracing.adoc index cb1114415cb..cf9e5099228 100644 --- a/docs/mp/tracing.adoc +++ b/docs/mp/tracing.adoc @@ -47,6 +47,7 @@ and Security using either the https://zipkin.io[Zipkin] or https://www.jaegertra include::{rootdir}/includes/dependencies.adoc[] +// tag::tracing-dependency[] [source,xml] ---- @@ -54,6 +55,7 @@ include::{rootdir}/includes/dependencies.adoc[] helidon-microprofile-tracing ---- +// end::tracing-dependency[] == Usage diff --git a/docs/se/tracing/tracing.adoc b/docs/se/tracing/tracing.adoc index 4250d975ea7..a216acde72d 100644 --- a/docs/se/tracing/tracing.adoc +++ b/docs/se/tracing/tracing.adoc @@ -25,6 +25,7 @@ include::{rootdir}/includes/se.adoc[] include::{rootdir}/includes/dependencies.adoc[] +// tag::tracing-dependency[] [source,xml] ---- @@ -32,6 +33,7 @@ include::{rootdir}/includes/dependencies.adoc[] helidon-tracing ---- +// end::tracing-dependency[] == Tracing Support Helidon includes support for tracing through the https://opentracing.io/[OpenTracing] APIs. diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 629b4be9921..a576e4cff0e 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -424,10 +424,10 @@ backend: type: "icon" value: "av_timer" sources: - - "introduction.adoc" - - "metrics-capable-components.adoc" + - "metrics.adoc" - "micrometer.adoc" - "prometheus-exemplar-support.adoc" + - "metrics-capable-components.adoc" - type: "PAGE" title: "OpenAPI" source: "openapi.adoc" diff --git a/integrations/micrometer/micrometer/pom.xml b/integrations/micrometer/micrometer/pom.xml index 9a5a62b38f6..2b02785df18 100644 --- a/integrations/micrometer/micrometer/pom.xml +++ b/integrations/micrometer/micrometer/pom.xml @@ -73,6 +73,18 @@ io.helidon.common helidon-common-http + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + org.junit.jupiter junit-jupiter-api diff --git a/integrations/micrometer/micrometer/src/main/java/io/helidon/integrations/micrometer/MicrometerSupport.java b/integrations/micrometer/micrometer/src/main/java/io/helidon/integrations/micrometer/MicrometerSupport.java index ff4f993e9b4..a89bc86d3c0 100644 --- a/integrations/micrometer/micrometer/src/main/java/io/helidon/integrations/micrometer/MicrometerSupport.java +++ b/integrations/micrometer/micrometer/src/main/java/io/helidon/integrations/micrometer/MicrometerSupport.java @@ -19,6 +19,7 @@ import java.util.logging.Logger; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; import io.helidon.servicecommon.rest.HelidonRestServiceSupport; import io.helidon.webserver.Handler; import io.helidon.webserver.Routing; @@ -119,6 +120,7 @@ private void getOrOptions(ServerRequest serverRequest, ServerResponse serverResp /** * Fluid builder for {@code MicrometerSupport} objects. */ + @Configured(prefix = "micrometer") public static class Builder extends HelidonRestServiceSupport.Builder implements io.helidon.common.Builder { diff --git a/integrations/micrometer/micrometer/src/main/java/module-info.java b/integrations/micrometer/micrometer/src/main/java/module-info.java index e09540ec237..1d7efdf9796 100644 --- a/integrations/micrometer/micrometer/src/main/java/module-info.java +++ b/integrations/micrometer/micrometer/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -28,6 +28,8 @@ requires io.helidon.config; requires io.helidon.webserver.cors; + requires static io.helidon.config.metadata; + requires micrometer.core; requires micrometer.registry.prometheus; requires simpleclient; diff --git a/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java b/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java index e86c038e860..88ee8923251 100644 --- a/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java +++ b/metrics/api/src/main/java/io/helidon/metrics/api/MetricsSettings.java @@ -214,7 +214,7 @@ interface Builder extends io.helidon.common.Builder { * @return updated builder */ @ConfiguredOption(key = REGISTRIES_CONFIG_KEY, - kind = ConfiguredOption.Kind.LIST, + kind = ConfiguredOption.Kind.MAP, type = RegistrySettings.class) Builder registrySettings(MetricRegistry.Type registryType, RegistrySettings registrySettings); diff --git a/metrics/service-api/pom.xml b/metrics/service-api/pom.xml index 561f96711ac..926309f9d90 100644 --- a/metrics/service-api/pom.xml +++ b/metrics/service-api/pom.xml @@ -50,6 +50,18 @@ io.helidon.service-common helidon-service-common-rest + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + org.junit.jupiter junit-jupiter-api diff --git a/metrics/service-api/src/main/java/io/helidon/metrics/serviceapi/MetricsSupport.java b/metrics/service-api/src/main/java/io/helidon/metrics/serviceapi/MetricsSupport.java index a37d84bd217..0fd2d74afdf 100644 --- a/metrics/service-api/src/main/java/io/helidon/metrics/serviceapi/MetricsSupport.java +++ b/metrics/service-api/src/main/java/io/helidon/metrics/serviceapi/MetricsSupport.java @@ -16,6 +16,7 @@ package io.helidon.metrics.serviceapi; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; import io.helidon.config.metadata.ConfiguredOption; import io.helidon.metrics.api.MetricsSettings; import io.helidon.servicecommon.rest.RestServiceSettings; @@ -151,6 +152,8 @@ static RestServiceSettings.Builder defaultedMetricsRestServiceSettingsBuilder() * @param builder type * @param specific implementation type of {@code MetricsSupport} */ + + @Configured interface Builder, T extends MetricsSupport> extends io.helidon.common.Builder { /** @@ -166,6 +169,8 @@ interface Builder, T extends MetricsSupport> extends io. * @param metricsSettingsBuilder the metrics settings to assign for use in building the {@code MetricsSupport} instance * @return updated builder */ + @ConfiguredOption(mergeWithParent = true, + type = MetricsSettings.class) B metricsSettings(MetricsSettings.Builder metricsSettingsBuilder); /** @@ -175,7 +180,7 @@ interface Builder, T extends MetricsSupport> extends io. * @return updated builder */ @ConfiguredOption(mergeWithParent = true, - kind = ConfiguredOption.Kind.MAP) + type = RestServiceSettings.class) B restServiceSettings(RestServiceSettings.Builder restServiceSettingsBuilder); } } From cf78326a8cc4c063cafbcf0639dcc9c0530b5647 Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Thu, 14 Jul 2022 23:50:11 -0700 Subject: [PATCH 22/51] update etc/scripts/includes/oci.sh to work outside of pipelines (#4550) * update etc/scripts/includes/oci.sh to work outside of pipelines * Update usage of flock * add more XXX to mktemp --- etc/scripts/includes/oci.sh | 259 ++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 146 deletions(-) diff --git a/etc/scripts/includes/oci.sh b/etc/scripts/includes/oci.sh index a4952cd90c8..e3f55e90c85 100644 --- a/etc/scripts/includes/oci.sh +++ b/etc/scripts/includes/oci.sh @@ -33,7 +33,7 @@ if [ -z "${WS_DIR}" ]; then exit 1 fi - readonly WS_DIR=$(cd $(dirname -- "${1}") ; cd "${2}" ; pwd -P) + readonly WS_DIR=$(cd "$(dirname -- ${1})" ; cd "${2}" ; pwd -P) fi @@ -41,6 +41,99 @@ fi if [ -z "${__OCI_INCLUDED__}" ]; then readonly __OCI_INCLUDED__='true' + # The presumed artifact ID of the shaded full jar + # as defined in https://github.com/oracle/oci-java-sdk/blob/v2.35.0/bmc-shaded/bmc-shaded-full/pom.xml + readonly OCI_SHADED_FULL_ARTIFACT_ID="oci-java-sdk-shaded-full" + + # evaluate a maven property + # arg: property to evaluate + mvn_eval() { + mvn ${MAVEN_ARGS} -q -N -f "${WS_DIR}/pom.xml" help:evaluate -Dexpression="${1}" -DforceStdout + } + + # download the oci shaded jar + # arg1: oci_version + # arg2: target path + download_shaded_jar() { + + # See + # https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/javasdkgettingstarted.htm + # https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/javasdkexamples.htm + + local oci_version + oci_version="${1}" + + local target_path + target_path="${2}" + + local zip_filename + zip_filename="oci-java-sdk-${oci_version}.zip" + + local oci_zip_url + oci_zip_url="https://github.com/oracle/oci-java-sdk/releases/download/v${oci_version}/${zip_filename}" + + local jar_path + jar_path="shaded/lib/oci-java-sdk-full-shaded-${oci_version}.jar" + + local oci_zip_file + oci_zip_file="$(mktemp -t XXX-oci-sdk)" + + # Download the all-in-one zip file + # if that works, unzip oci-java-sdk-full-shaded-${oci_version}.jar contained inside it. + curl -L -s -S -o "${oci_zip_file}" "${oci_zip_url}" && \ + unzip -j -n -p -q "${oci_zip_file}" "${jar_path}" > "${target_path}" && \ + rm "${oci_zip_file}" + } + + # install a jar in the local maven repository + # arg1: file + # arg2: groupId + # arg3: artifactId + # arg4: version + mvn_install_jar() { + mvn ${MAVEN_ARGS} -N -q \ + -f "${WS_DIR}/pom.xml" \ + install:install-file \ + -Dfile="${1}" \ + -DgroupId="${2}" \ + -DartifactId="${3}" \ + -Dversion="${4}" \ + -Dpackaging="jar" + } + + # download and install the oci shaded jar + # arg1: artifact_file + # arg2: oci_version + _install_oci_shaded_full_jar() { + local artifact_file + artifact_file="${1}" + + local oci_version + oci_version="${2}" + + if [ -e "${artifact_file}" ]; then + # already installed + return 0 + fi + + local cache_dir + if [ -n "${JENKINS_HOME}" ] ; then + cache_dir="/cache" + else + cache_dir="$(mktemp -d)" + fi + + local jar_file + jar_file="${cache_dir}/$(basename ${artifact_file})" + if [ ! -e "${jar_file}" ]; then + echo "Downloading OCI SDK v${oci_version}" + download_shaded_jar "${oci_version}" "${jar_file}" + fi + + echo "Installing OCI SDK v${oci_version}" + mvn_install_jar "${jar_file}" "com.oracle.oci.sdk" "${OCI_SHADED_FULL_ARTIFACT_ID}" "${oci_version}" + } + # # Downloads and installs the OCI Java SDK's shaded full jar into # the local Maven repository if it is not already present. @@ -49,155 +142,29 @@ if [ -z "${__OCI_INCLUDED__}" ]; then # install_oci_shaded_full_jar() { - # The presumed artifact ID of the shaded full jar, as defined - # in, e.g., - # https://github.com/oracle/oci-java-sdk/blob/v2.35.0/bmc-shaded/bmc-shaded-full/pom.xml#L10, - # although that that pom.xml file seems to be a stub of sorts. - # We anticipate this will be the artifact ID of the shaded - # full jar that OCI will eventually push to Maven Central, - # rendering this workaround moot. See also - # https://github.com/oracle/oci-java-sdk/issues/371#issuecomment-1086331705. - # - # Note carefully that this is different from the shaded jar's - # location in the .zip file for some reason. - local OCI_SHADED_FULL_ARTIFACT_ID - readonly OCI_SHADED_FULL_ARTIFACT_ID=oci-java-sdk-shaded-full - - # The version of OCI in use. - if [ -z "${OCI_VERSION}" ]; then - local OCI_VERSION - readonly OCI_VERSION="$(mvn ${MAVEN_ARGS} --file "${WS_DIR}/pom.xml" --non-recursive --quiet org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=version.lib.oci -DforceStdout)" - fi + local oci_version + oci_version="$(mvn_eval 'version.lib.oci')" - # The relative name of the shaded full jar as implied by the - # artifact ID above. - local OCI_SHADED_FULL_JAR - readonly OCI_SHADED_FULL_JAR="${OCI_SHADED_FULL_ARTIFACT_ID}-${OCI_VERSION}.jar" + local jar_filename + readonly jar_filename="${OCI_SHADED_FULL_ARTIFACT_ID}-${oci_version}.jar" - # Figure out where the local Maven repository (cache) is. - if [ -z "${MAVEN_REPO_LOCAL}" ]; then - local MAVEN_REPO_LOCAL - readonly MAVEN_REPO_LOCAL="$(mvn ${MAVEN_ARGS} --file "${WS_DIR}/pom.xml" --non-recursive --quiet help:evaluate -Dexpression=settings.localRepository -DforceStdout)" - fi + local maven_repo_local + maven_repo_local="$(mvn_eval 'settings.localRepository')" + + local artifact_dir + artifact_dir="${maven_repo_local}/com/oracle/oci/sdk/${OCI_SHADED_FULL_ARTIFACT_ID}/${oci_version}" - if [ ! -e "${MAVEN_REPO_LOCAL}/com/oracle/oci/sdk/${OCI_SHADED_FULL_ARTIFACT_ID}/${OCI_VERSION}/${OCI_SHADED_FULL_JAR}" ]; then - - # If the local Maven repository (cache) doesn't already - # have the shaded full jar, it's time to install it. - - # Set where to download it (if downloading turns out to be - # necessary). The containing directory is assumed to be - # writeable. In the Helidon pipelines environment, this - # assumption will always be true. - if [ -z "${CACHED_OCI_SHADED_FULL_JAR}" ]; then - local CACHED_OCI_SHADED_FULL_JAR - readonly CACHED_OCI_SHADED_FULL_JAR="/cache/${OCI_SHADED_FULL_JAR}" - fi - - if [ ! -e "${CACHED_OCI_SHADED_FULL_JAR}" ]; then - - # The downloaded shaded full jar does not exist. It - # is time to download it. To do this, we have to - # download the all-in-one .zip file that OCI publishes - # to their Github repository, and then extract the - # shaded full jar from that. See - # https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/javasdkgettingstarted.htm#:~:text=Oracle%20development%20tools.-,Downloading%20the%20SDK%20from%20GitHub,-You%20can%20download - # to start, and then - # https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/javasdkexamples.htm#:~:text=your%20own%20environment.-,Running%20Examples,-Download%20the%20SDK - # and - # https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/javasdkexamples.htm#:~:text=Third%2DParty%20Dependencies%20and%20Shading. - - # The relative name of the OCI SDK zip file once it - # has been downloaded. - local OCI_ZIP - readonly OCI_ZIP="oci-java-sdk-${OCI_VERSION}.zip" - - # The (usually Github) URI identifying the full OCI - # SDK zip file to be downloaded. - if [ -z "${OCI_ZIP_URI}" ]; then - local OCI_ZIP_URI - readonly OCI_ZIP_URI="https://github.com/oracle/oci-java-sdk/releases/download/v${OCI_VERSION}/${OCI_ZIP}" - fi - - # A directory where a lock file to be used by flock(1) - # will be placed. The directory is presumed to exist. - if [ -z "${LOCKFILE_DIR}" ]; then - local LOCKFILE_DIR; - readonly LOCKFILE_DIR=$(dirname -- "${CACHED_OCI_SHADED_FULL_JAR}") - fi - - # Run flock(1) to acquire an exclusive lock on the - # lowest-numbered unused file descriptor greater than - # or equal to 10, waiting ten minutes (600 seconds) if - # necessary before giving up, and perform the - # download. See - # https://www.gnu.org/software/bash/manual/bash.html#Redirections:~:text=Each%20redirection%20that%20may%20be%20preceded%20by%20a%20file%20descriptor%20number%20may%20instead%20be%20preceded%20by%20a%20word%20of%20the%20form%20%7Bvarname%7D. and - # https://stackoverflow.com/questions/8297415/in-bash-how-to-find-the-lowest-numbered-unused-file-descriptor#comment39126452_17030546. - # - # Recall that the file being downloaded is nearly a - # gigabyte in size so this can take a great deal of - # time. - # - # Note below that the file descriptor is redirected to - # ${LOCKFILE_DIR}/${OCI_ZIP}.download.lock. flock(1) - # on recent kernels will apparently work even in the - # presence of NFS, which is most likely where - # ${LOCKFILE_DIR} is mounted. - local LOCKFILE_FD - ( flock --exclusive --timeout 600 ${LOCKFILE_FD} || exit 1 - - # The temporary directory into which ${OCI_ZIP_URI}'s - # referent will be downloaded. - # - # For storage planning purposes, note that this file - # is about 721 MB. It is (remotely) conceivable that - # n jobs could be running that reference different n - # different OCI versions, so there could be the need - # for n * 721 MB of space for these downloads. - local OCI_ZIP_TEMPDIR - readonly OCI_ZIP_TEMPDIR="$(mktemp -d)" - - # Download the all-in-one zip file, and, if that - # works, unzip the - # oci-java-sdk-full-shaded-${OCI_VERSION}.jar file - # contained inside it quietly (-q) to stdout (-p) - # discarding path information (-j), and making sure - # not to ever query or overwrite (-n) redirect its - # contents into ${CACHED_OCI_SHADED_FULL_JAR}, and, if - # that works, remove the .zip file we downloaded and - # any temporary directories created along the way. - # The end result will be - # ${CACHED_OCI_SHADED_FULL_JAR}. - # - # For storage planning purposes, the shaded full jar - # is approximately 106 MB in size. - # - # Note that for some reason the shaded full jar is - # present in the all-in-one zip file as - # shaded/lib/oci-java-sdk-full-shaded..., not, as you - # might expect, - # shaded/lib/oci-java-sdk-shaded-full.... That is not - # a mistake or a typo. - curl --location --output "${OCI_ZIP_TEMPDIR}/${OCI_ZIP}" --show-error --silent "${OCI_ZIP_URI}" && \ - unzip -j -n -p -q "${OCI_ZIP_TEMPDIR}/${OCI_ZIP}" "shaded/lib/oci-java-sdk-full-shaded-${OCI_VERSION}.jar" > "${CACHED_OCI_SHADED_FULL_JAR}" && \ - rm "${OCI_ZIP_TEMPDIR}/${OCI_ZIP}" && \ - rmdir "${OCI_ZIP_TEMPDIR}" - - ) {LOCKFILE_FD}>"${LOCKFILE_DIR}/${OCI_ZIP}.download.lock" # the braces without $ around FD are on purpose - - fi - - # Install the cached shaded full jar into the local Maven - # repository (cache) since we determined that it did not - # exist prior to all this. - mvn ${MAVEN_ARGS} --file "${WS_DIR}/pom.xml" --non-recursive --quiet org.apache.maven.plugins:maven-install-plugin:install-file \ - -Dfile="${CACHED_OCI_SHADED_FULL_JAR}" \ - -DlocalRepositoryPath="${MAVEN_REPO_LOCAL}" \ - -DgroupId=com.oracle.oci.sdk \ - -DartifactId="${OCI_SHADED_FULL_ARTIFACT_ID}" \ - -Dversion="${OCI_VERSION}" \ - -Dpackaging=jar + if [ -e "${artifact_dir}/${jar_filename}" ]; then + return 0 + fi + if [ -n "${JENKINS_HOME}" ] ; then + ( + flock -w 600 200 + _install_oci_shaded_full_jar "${artifact_dir}/${jar_filename}" "${oci_version}" + ) 200>"${artifact_dir}/lock" + else + _install_oci_shaded_full_jar "${artifact_dir}/${jar_filename}" "${oci_version}" fi } else From 831992b5c6241d130a5b0189db99f675338a36d0 Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Fri, 15 Jul 2022 04:32:36 -0400 Subject: [PATCH 23/51] New formatting and content for Helidon GraphQL SE Server document (#4478) * New formatting for Helidon GraphQL SE Server document. * Updated Configuration section to use an include. * Removed experimental note. Signed-off-by: Santiago Pericasgeertsen --- docs/includes/attributes.adoc | 1 + docs/includes/graphql.adoc | 4 -- docs/mp/graphql.adoc | 5 +-- docs/se/graphql.adoc | 82 ++++++++++++++++++++++------------- 4 files changed, 53 insertions(+), 39 deletions(-) diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 4a6cc44808f..77b2c6cdfc5 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -197,6 +197,7 @@ endif::[] :webserver-jersey-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.jersey :webserver-staticcontent-javadoc-base-url: {webserver-javadoc-base-url}.staticcontent :webserver-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.cors +:graphql-javadoc-base-url: {javadoc-base-url}/io.helidon.graphql.server // 3rd party versioned URLs :jaeger-doc-base-url: https://www.jaegertracing.io/docs/{version-lib-jaeger} diff --git a/docs/includes/graphql.adoc b/docs/includes/graphql.adoc index ed35f1a8190..340367624d8 100644 --- a/docs/includes/graphql.adoc +++ b/docs/includes/graphql.adoc @@ -18,10 +18,6 @@ ifndef::rootdir[:rootdir: {docdir}/..] -=== Helidon GraphQL -In addition, we provide the following configuration options: - - The following configuration keys can be used to set up integration with WebServer: [cols="2,2,5"] diff --git a/docs/mp/graphql.adoc b/docs/mp/graphql.adoc index 54aeddfc334..45a8ecd04a5 100644 --- a/docs/mp/graphql.adoc +++ b/docs/mp/graphql.adoc @@ -25,7 +25,7 @@ include::{rootdir}/includes/mp.adoc[] -== ToC +== Contents - <> - <> @@ -186,9 +186,6 @@ The specification defines the following configuration options: |=== -These configuration options are more significant that the configuration options -that can be used to configure GraphQL invocation (see below). - include::{rootdir}/includes/graphql.adoc[] == Examples diff --git a/docs/se/graphql.adoc b/docs/se/graphql.adoc index 093c5106262..ff8b548179d 100644 --- a/docs/se/graphql.adoc +++ b/docs/se/graphql.adoc @@ -24,12 +24,22 @@ include::{rootdir}/includes/se.adoc[] -Helidon GraphQL Server provides a framework for creating link:https://github.com/graphql-java/graphql-java[GraphQL] applications. -== Experimental +== Contents -WARNING: The Helidon GraphQL feature is currently experimental and the APIs are - subject to changes until GraphQL support is stabilized. +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +The Helidon GraphQL Server provides a framework for creating link:https://github.com/graphql-java/graphql-java[GraphQL] +applications that integrate with the Helidon WebServer. GraphQL is a query language to access server data. +The Helidon GraphQL integration enables HTTP clients to issue queries over the network and retrieve data; +it is an alternative to other protocols such as REST or GRPC. include::{rootdir}/includes/dependencies.adoc[] @@ -41,30 +51,32 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Quick Start +== API + +An instance of `GraphQlSupport` must be registered in the Helidon WebServer routes to enable +GraphQL support in your application. In addition, a GraphQL schema needs to be specified +to verify and execute queries. -Here is the code for a minimalist GraphQL application that exposes 2 queries. +The following code fragment creates an instance of `GraphQlSupport` and registers it in the +Helidon WebServer. [source,java] ---- -public static void main(String[] args) { WebServer server = WebServer.builder() .routing(Routing.builder() - .register(GraphQlSupport.create(buildSchema())) <1> - .build()) + .register(GraphQlSupport.create(buildSchema())) + .build()) .build(); +---- - server.start() <2> - .thenApply(webServer -> { - String endpoint = "http://localhost:" + webServer.port(); - System.out.println("GraphQL started on " + endpoint + "/graphql"); - System.out.println("GraphQL schema availanle on " + endpoint + "/graphql/schema.graphql"); - return null; - }); -} +By default, `GraphQlSupport` will reserve `/graphql` as the URI path to process queries. +The `buildSchema` method creates the schema and defines 2 types of queries for this +application: +[source,java] +---- private static GraphQLSchema buildSchema() { - String schema = "type Query{\n" <3> + String schema = "type Query{\n" <1> + "hello: String \n" + "helloInDifferentLanguages: [String] \n" + "\n}"; @@ -72,32 +84,37 @@ private static GraphQLSchema buildSchema() { SchemaParser schemaParser = new SchemaParser(); TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema); - // DataFetcher to return various hello's in difference languages <4> + // DataFetcher to return various hellos in difference languages <2> DataFetcher> hellosDataFetcher = (DataFetcher>) environment -> - List.of("Bonjour", "Hola", "Zdravstvuyte", "Nǐn hǎo", "Salve", "Gudday", "Konnichiwa", "Guten Tag"); + List.of("Bonjour", "Hola", "Zdravstvuyte", "Nǐn hǎo", "Salve", "Gudday", + "Konnichiwa", "Guten Tag"); - RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring() <5> + RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring() <3> .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world"))) .type("Query", builder -> builder.dataFetcher("helloInDifferentLanguages", hellosDataFetcher)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); - return schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); <6> + return schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); <4> } ---- -<1> Register GraphQL support. -<2> Start the server. -<3> Define the GraphQL schema. -<4> Create a DataFetcher to return a List of Hellos in different languages. -<5> Wire up the DataFetchers. -<6> Generate the GraphQL schema. +The following is a description of each of these steps: + +<1> Define the GraphQL schema. +<2> Create a `DataFetcher` to return a list of hellos in different languages. +<3> Wire up the `DataFetcher` s. +<4> Generate the GraphQL schema. + +== Configuration -The example above deploys a very simple service exposing the `/graphql` endpoint. +include::{rootdir}/includes/graphql.adoc[] -You can then probe the endpoints: +== Examples -1. Hello word endpoint +Using the schema defined in Section <>, you can probe the following endpoints: + +1. Hello world endpoint + [source,bash] ---- @@ -115,3 +132,6 @@ curl -X POST http://127.0.0.1:PORT/graphql -d '{"query":"query { helloInDifferen {"data":{"helloInDifferentLanguages":["Bonjour","Hola","Zdravstvuyte","Nǐn hǎo","Salve","Gudday","Konnichiwa","Guten Tag"]}} ---- +== Additional Information + + * link:{graphql-javadoc-base-url}/module-summary.html[GraphQL Javadocs] \ No newline at end of file From 1a215976d153af3adfce1f5f74062a4865ee587e Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Fri, 15 Jul 2022 11:58:05 +0300 Subject: [PATCH 24/51] [New Doc PR] - SE Guides Menu Inconsistencies #4510 (#4527) * Add missing cards to guides * Fix comments --- docs/se/guides/overview.adoc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/se/guides/overview.adoc b/docs/se/guides/overview.adoc index 022c2a28057..31c623792bb 100644 --- a/docs/se/guides/overview.adoc +++ b/docs/se/guides/overview.adoc @@ -67,6 +67,41 @@ Learn how to use Helidon SE built-in and application metrics. -- Learn how to trace a Helidon SE application. -- + +[CARD] +.OIDC Guide +[link=security-oidc.adoc] +-- +Learn how to set up an OIDC Helidon SE application +-- + +[CARD] +.Helidon SE Upgrade Guide +[link=migration.adoc] +-- +Learn how to Upgrade your Helidon SE application +-- + +[CARD] +.Helidon SE WebClient Guide +[link=webclient.adoc] +-- +Learn how to use the Helidon SE Reactive WebClient +-- + +[CARD] +.Helidon SE DB Client Guide +[link=dbclient.adoc] +-- +Learn how to use the Helidon SE Reactive DB Client +-- + +[CARD] +.Helidon SE Performance Tuning Guide +[link=performance-tuning.adoc] +-- +Learn how to tune your Helidon SE application +-- ==== == Build and Deploy From bbc65c15b4bf76abb5dadb7551cac2f0aa4a89c6 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 15 Jul 2022 14:30:46 +0200 Subject: [PATCH 25/51] Tracing documentation update. (#4555) Signed-off-by: Tomas Langer --- docs/config/config_reference.adoc | 91 ++++++------ docs/config/io_helidon_tracing_Tracer.adoc | 8 ++ .../io_helidon_tracing_TracerBuilder.adoc | 2 +- ...on_tracing_jaeger_JaegerTracerBuilder.adoc | 75 ++++++++++ ...on_tracing_zipkin_ZipkinTracerBuilder.adoc | 69 +++++++++ .../metrics/prometheus-exemplar-support.adoc | 6 +- docs/includes/tracing/jaeger-metrics.adoc | 90 ------------ docs/includes/tracing/tracer-jaeger.adoc | 70 +-------- docs/includes/tracing/tracer-zipkin.adoc | 23 +-- docs/mp/tracing.adoc | 130 ++++------------- docs/se/introduction.adoc | 2 +- docs/se/{tracing => }/tracing.adoc | 134 ++++++++++++++---- docs/se/tracing/jaeger-metrics.adoc | 25 ---- docs/se/tracing/jaeger.adoc | 26 ---- docs/se/tracing/zipkin.adoc | 25 ---- docs/sitegen.yaml | 9 +- .../tracing/jaeger/JaegerTracerBuilder.java | 7 + tracing/opentracing/pom.xml | 24 ++++ .../opentracing/OpenTracingTracerBuilder.java | 13 ++ .../src/main/java/module-info.java | 1 + .../io/helidon/tracing/TracerBuilder.java | 2 +- tracing/zipkin/pom.xml | 24 ++++ .../tracing/zipkin/ZipkinTracerBuilder.java | 4 + tracing/zipkin/src/main/java/module-info.java | 1 + 24 files changed, 425 insertions(+), 436 deletions(-) create mode 100644 docs/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc create mode 100644 docs/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc delete mode 100644 docs/includes/tracing/jaeger-metrics.adoc rename docs/se/{tracing => }/tracing.adoc (60%) delete mode 100644 docs/se/tracing/jaeger-metrics.adoc delete mode 100644 docs/se/tracing/jaeger.adoc delete mode 100644 docs/se/tracing/zipkin.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index b74a015de9d..4b862222931 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -24,49 +24,50 @@ ifndef::rootdir[:rootdir: {docdir}/..] The following section lists all configurable types in Helidon. -- xref:{rootdir}/config/io_helidon_security_providers_abac_AbacProvider.adoc[AbacProvider.adoc (security.providers.abac)] -- xref:{rootdir}/config/io_helidon_metrics_api_BaseMetricsSettings.adoc[BaseMetricsSettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_faulttolerance_Bulkhead.adoc[Bulkhead.adoc (faulttolerance)] -- xref:{rootdir}/config/io_helidon_faulttolerance_CircuitBreaker.adoc[CircuitBreaker.adoc (faulttolerance)] -- xref:{rootdir}/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc[ComponentMetricsSettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser.adoc (security.providers.httpauth.ConfigUserStore)] -- xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig.adoc (webserver.cors)] -- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc[DelayingRetryPolicy.adoc (faulttolerance.Retry)] -- xref:{rootdir}/config/io_helidon_health_HealthSupport.adoc[HealthSupport.adoc (health)] -- xref:{rootdir}/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc[HelidonRestServiceSupport.adoc (servicecommon.rest)] -- xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[HttpBasicAuthProvider.adoc (security.providers.httpauth)] -- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc[JitterRetryPolicy.adoc (faulttolerance.Retry)] -- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig.adoc (common.pki)] -- xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc[KeystoreBuilder.adoc (common.pki.KeyConfig)] -- xref:{rootdir}/config/io_helidon_common_configurable_LruCache.adoc[LruCache.adoc (common.configurable)] +- xref:{rootdir}/config/io_helidon_security_providers_abac_AbacProvider.adoc[AbacProvider (security.providers.abac)] +- xref:{rootdir}/config/io_helidon_metrics_api_BaseMetricsSettings.adoc[BaseMetricsSettings (metrics.api)] +- 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_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser (security.providers.httpauth.ConfigUserStore)] +- xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig (webserver.cors)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc[DelayingRetryPolicy (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_health_HealthSupport.adoc[HealthSupport (health)] +- xref:{rootdir}/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc[HelidonRestServiceSupport (servicecommon.rest)] +- xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[HttpBasicAuthProvider (security.providers.httpauth)] +- xref:{rootdir}/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc[JaegerTracer (tracing.jaeger)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc[JitterRetryPolicy (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig (common.pki)] +- xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings (metrics.api)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc[KeystoreBuilder (common.pki.KeyConfig)] +- xref:{rootdir}/config/io_helidon_common_configurable_LruCache.adoc[LruCache (common.configurable)] - xref:{rootdir}/config/io_helidon_media_common_MediaContext.adoc[MediaContext (media.common)] -- xref:{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[MPOpenAPISupport.adoc (microprofile.openapi)] -- xref:{rootdir}/config/io_helidon_metrics_api_MetricsSettings.adoc[MetricsSettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc[MetricsSupport.adoc (metrics.serviceapi)] -- xref:{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[MicrometerSupport.adoc (integrations.micrometer)] -- xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig.adoc (security.providers.oidc.common)] -- xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[OidcProvider.adoc (security.providers.oidc)] -- xref:{rootdir}/config/io_helidon_openapi_OpenAPISupport.adoc[OpenAPISupport.adoc (openapi)] -- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig.adoc (security.providers.common)] -- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget.adoc (security.providers.common)] -- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc[PemBuilder.adoc (common.pki.KeyConfig)] -- xref:{rootdir}/config/io_helidon_metrics_api_RegistryFilterSettings.adoc[RegistryFilterSettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[RegistrySettings.adoc (metrics.api)] -- xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource.adoc (common.configurable)] -- xref:{rootdir}/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc[RestServiceSettings.adoc (servicecommon.rest)] -- xref:{rootdir}/config/io_helidon_faulttolerance_Retry.adoc[Retry.adoc (faulttolerance)] -- xref:{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[SEOpenAPISupport.adoc (openapi)] -- xref:{rootdir}/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc[ScheduledThreadPoolSupplier.adoc (common.configurable)] -- xref:{rootdir}/config/io_helidon_security_Security.adoc[Security.adoc (security)] -- xref:{rootdir}/config/io_helidon_security_SecurityTime.adoc[SecurityTime.adoc (security)] -- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration.adoc[SocketConfiguration.adoc (webserver)] -- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc[SocketConfigurationBuilder.adoc (webserver.SocketConfiguration)] -- xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc[ThreadPoolSupplier.adoc (common.configurable)] -- xref:{rootdir}/config/io_helidon_faulttolerance_Timeout.adoc[Timeout.adoc (faulttolerance)] -- xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler.adoc (security.util)] -- xref:{rootdir}/config/io_helidon_tracing_Tracer.adoc[Tracer.adoc (tracing)] -- xref:{rootdir}/config/io_helidon_tracing_TracerBuilder.adoc[TracerBuilder.adoc (tracing)] -- xref:{rootdir}/config/io_helidon_webserver_WebServer.adoc[WebServer.adoc (webserver)] -- xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls.adoc (webserver)] -- xref:{rootdir}/config/io_smallrye_openapi_api_OpenApiConfig.adoc[io_smallrye_openapi_api_OpenApiConfig.adoc] +- xref:{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[MPOpenAPISupport (microprofile.openapi)] +- xref:{rootdir}/config/io_helidon_metrics_api_MetricsSettings.adoc[MetricsSettings (metrics.api)] +- xref:{rootdir}/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc[MetricsSupport (metrics.serviceapi)] +- xref:{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[MicrometerSupport (integrations.micrometer)] +- xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig (security.providers.oidc.common)] +- xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[OidcProvider (security.providers.oidc)] +- xref:{rootdir}/config/io_helidon_openapi_OpenAPISupport.adoc[OpenAPISupport (openapi)] +- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig (security.providers.common)] +- xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget (security.providers.common)] +- xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc[PemBuilder (common.pki.KeyConfig)] +- xref:{rootdir}/config/io_helidon_metrics_api_RegistryFilterSettings.adoc[RegistryFilterSettings (metrics.api)] +- xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[RegistrySettings (metrics.api)] +- xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource (common.configurable)] +- xref:{rootdir}/config/io_helidon_servicecommon_rest_RestServiceSettings.adoc[RestServiceSettings (servicecommon.rest)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Retry.adoc[Retry (faulttolerance)] +- xref:{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[SEOpenAPISupport (openapi)] +- xref:{rootdir}/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc[ScheduledThreadPoolSupplier (common.configurable)] +- xref:{rootdir}/config/io_helidon_security_Security.adoc[Security (security)] +- xref:{rootdir}/config/io_helidon_security_SecurityTime.adoc[SecurityTime (security)] +- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration.adoc[SocketConfiguration (webserver)] +- xref:{rootdir}/config/io_helidon_webserver_SocketConfiguration_SocketConfigurationBuilder.adoc[SocketConfigurationBuilder (webserver.SocketConfiguration)] +- xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc[ThreadPoolSupplier (common.configurable)] +- xref:{rootdir}/config/io_helidon_faulttolerance_Timeout.adoc[Timeout (faulttolerance)] +- xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler (security.util)] +- xref:{rootdir}/config/io_helidon_tracing_TracerBuilder.adoc[TracerBuilder (tracing)] +- xref:{rootdir}/config/io_helidon_webserver_WebServer.adoc[WebServer (webserver)] +- xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls (webserver)] +- xref:{rootdir}/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc[ZipkinTracer (tracing.zipkin)] +- xref:{rootdir}/config/io_smallrye_openapi_api_OpenApiConfig.adoc[io_smallrye_openapi_api_OpenApiConfig] diff --git a/docs/config/io_helidon_tracing_Tracer.adoc b/docs/config/io_helidon_tracing_Tracer.adoc index 5e3648fa503..d365d6abfc3 100644 --- a/docs/config/io_helidon_tracing_Tracer.adoc +++ b/docs/config/io_helidon_tracing_Tracer.adoc @@ -47,8 +47,10 @@ Optional configuration options: |key |type |default value |description |`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`client-cert-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Certificate of client in PEM format. |`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should use a no-op tracer. +|`exporter-timeout-millis` |Duration |`10000` |Timeout of exporter requests. |`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. |`host` |string |{nbsp} |Host to use to connect to tracing collector. Default is defined by each tracing integration. @@ -57,10 +59,16 @@ Optional configuration options: Default is defined by each tracing integration. |`port` |int |{nbsp} |Port to use to connect to tracing collector. Default is defined by each tracing integration. +|`private-key-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Private key in PEM format. |`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. Default is defined by each tracing integration. +|`sampler-param` |Number |`1` |The sampler parameter (number). +|`sampler-type` |SamplerType (CONSTANT, RATIO) |`CONSTANT` |Sampler type. + + See Sampler types. |`service` |string |{nbsp} |Service name of the traced service. |`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. +|`trusted-cert-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Trusted certificates in PEM format. |=== diff --git a/docs/config/io_helidon_tracing_TracerBuilder.adoc b/docs/config/io_helidon_tracing_TracerBuilder.adoc index c086d6fd179..76040d1064b 100644 --- a/docs/config/io_helidon_tracing_TracerBuilder.adoc +++ b/docs/config/io_helidon_tracing_TracerBuilder.adoc @@ -26,7 +26,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] -OpenTracing tracer configuration. +Tracer configuration. Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/TracerBuilder.html[io.helidon.tracing.TracerBuilder] diff --git a/docs/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc b/docs/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc new file mode 100644 index 00000000000..d365d6abfc3 --- /dev/null +++ b/docs/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.tracing.Tracer +:keywords: helidon, config, io.helidon.tracing.Tracer +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.tracing.Tracer +include::{rootdir}/includes/attributes.adoc[] + += Tracer (tracing) Configuration + +// tag::config[] + +Jaeger tracer configuration. + + +Type: link:{javadoc-base-url}/io.helidon.tracing/io/helidon/tracing/Tracer.html[io.helidon.tracing.Tracer] + + +This is a standalone configuration type, prefix from configuration root: `tracing` + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`client-cert-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Certificate of client in PEM format. +|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should + use a no-op tracer. +|`exporter-timeout-millis` |Duration |`10000` |Timeout of exporter requests. +|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. +|`host` |string |{nbsp} |Host to use to connect to tracing collector. + Default is defined by each tracing integration. +|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. +|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. + Default is defined by each tracing integration. +|`port` |int |{nbsp} |Port to use to connect to tracing collector. + Default is defined by each tracing integration. +|`private-key-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Private key in PEM format. +|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. + Default is defined by each tracing integration. +|`sampler-param` |Number |`1` |The sampler parameter (number). +|`sampler-type` |SamplerType (CONSTANT, RATIO) |`CONSTANT` |Sampler type. + + See Sampler types. +|`service` |string |{nbsp} |Service name of the traced service. +|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. +|`trusted-cert-pem` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Trusted certificates in PEM format. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc b/docs/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc new file mode 100644 index 00000000000..ae251c0ca2d --- /dev/null +++ b/docs/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.opentracing.Tracer +:keywords: helidon, config, io.opentracing.Tracer +:basic-table-intro: The table below lists the configuration keys that configure io.opentracing.Tracer +include::{rootdir}/includes/attributes.adoc[] + += io.opentracing.Tracer Configuration + +// tag::config[] + +Zipkin tracer configuration + + +Type: io.opentracing.Tracer + + +This is a standalone configuration type, prefix from configuration root: `tracing` + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`api-version` |Version (V1, V2) |`V2` |Version of Zipkin API to use. + Defaults to Version#V2. +|`boolean-tags` |Map<string, boolean> |{nbsp} |Tracer level tags that get added to all reported spans. +|`enabled` |boolean |`true` |When enabled, tracing will be sent. If enabled is false, tracing should + use a no-op tracer. +|`global` |boolean |`true` |When enabled, the created instance is also registered as a global tracer. +|`host` |string |{nbsp} |Host to use to connect to tracing collector. + Default is defined by each tracing integration. +|`int-tags` |Map<string, int> |{nbsp} |Tracer level tags that get added to all reported spans. +|`path` |string |{nbsp} |Path on the collector host to use when sending data to tracing collector. + Default is defined by each tracing integration. +|`port` |int |{nbsp} |Port to use to connect to tracing collector. + Default is defined by each tracing integration. +|`protocol` |string |{nbsp} |Protocol to use (such as `http` or `https`) to connect to tracing collector. + Default is defined by each tracing integration. +|`service` |string |{nbsp} |Service name of the traced service. +|`tags` |Map<string, string> |{nbsp} |Tracer level tags that get added to all reported spans. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/includes/metrics/prometheus-exemplar-support.adoc b/docs/includes/metrics/prometheus-exemplar-support.adoc index b38787ca994..06a24481202 100644 --- a/docs/includes/metrics/prometheus-exemplar-support.adoc +++ b/docs/includes/metrics/prometheus-exemplar-support.adoc @@ -60,9 +60,9 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -Also, include the Helidon integration module for a tracing implementation...for example +Also, include the Helidon integration module for a tracing implementation, see ifdef::se-flavor[] -xref:{rootdir}/se/tracing/zipkin.adoc[Helidon Zipkin] +xref:{rootdir}/se/tracing.adoc#zipkin-tracing[Helidon Zipkin] endif::[] ifdef::mp-flavor[] xref:{rootdir}/mp/tracing.adoc#zipkin-tracing[Helidon Zipkin]: @@ -72,7 +72,7 @@ include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-dependency] Add the Helidon tracing component itself: ifdef::se-flavor[] -include::{rootdir}/se/tracing/tracing.adoc[tag=tracing-dependency] +include::{rootdir}/se/tracing.adoc[tag=tracing-dependency] endif::[] ifdef::mp-flavor[] include::{rootdir}/mp/tracing.adoc[tag=tracing-dependency] diff --git a/docs/includes/tracing/jaeger-metrics.adoc b/docs/includes/tracing/jaeger-metrics.adoc deleted file mode 100644 index b952a339045..00000000000 --- a/docs/includes/tracing/jaeger-metrics.adoc +++ /dev/null @@ -1,90 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -ifndef::rootdir[:rootdir: {docdir}/../..] -ifndef::flavor-lc[:flavor-lc: se] -:description: Helidon metrics -:keywords: helidon, metrics, jaeger, tracing - -Integrate the metrics from Jaeger tracing into your Helidon {flavor-uc} application simply by adding a dependency. - -== Overview -As the xref:{rootdir}/{flavor-lc}/tracing/jaeger-metrics.adoc[Helidon Jaeger Tracing] document describes, you can -use Jaeger tracing in your Helidon {flavor-uc} application. - -Jaeger maintains several metrics about its own activity (briefly outlined in the -link:{jaeger-doc-base-url}/client-libraries#metrics[Jaeger client documentation]). - This document explains how you can integrate those Jaeger tracing metrics with Helidon's metrics. - -== Prerequisites -Your `pom.xml` file should already contain the dependency for Helidon-Jaeger tracing integration. - -To enable integration with Jaeger's metrics, add the following dependency: - -[source,xml,subs="verbatim,attributes"] ----- - - io.helidon.metrics - helidon-metrics-jaeger - runtime - ----- - -You can leave your application's Java code unchanged. -By adding this dependency, you instruct Helidon to monitor the Jaeger tracing metrics internally and to publish them using the Helidon metrics system. - -Rebuild and start your application. - -== Accessing Jaeger Tracing Metrics -Submit a few requests to your application's endpoints. -This causes Jaeger to update its internal metrics. - -Then, when you access your application's metrics endpoint (`/metrics` by default), Helidon displays the updated Jaeger tracing metrics as part of the `vendor` metrics section. - -[source,bash] ----- -curl -H "Accept: application/json" -X GET http://localhost:8080/metrics/vendor ----- -[source,json] -.Partial Helidon Metrics `vendor` Output Showing Jaeger Metrics ----- -{ - "jaeger_tracer_baggage_restrictions_updates;result=err": 0, - "jaeger_tracer_baggage_restrictions_updates;result=ok": 0, - "jaeger_tracer_baggage_truncations": 0, - "jaeger_tracer_baggage_updates;result=err": 0, - "jaeger_tracer_baggage_updates;result=ok": 0, - "jaeger_tracer_finished_spans": 0, - "jaeger_tracer_reporter_queue_length": 0, - "jaeger_tracer_reporter_spans;result=dropped": 0, - "jaeger_tracer_reporter_spans;result=err": 0, - "jaeger_tracer_reporter_spans;result=ok": 0, - "jaeger_tracer_sampler_queries;result=err": 1, - "jaeger_tracer_sampler_queries;result=ok": 0, - "jaeger_tracer_sampler_updates;result=err": 0, - "jaeger_tracer_sampler_updates;result=ok": 0, - "jaeger_tracer_span_context_decoding_errors": 0, - "jaeger_tracer_started_spans;sampled=n": 15, - "jaeger_tracer_started_spans;sampled=y": 0, - "jaeger_tracer_traces;sampled=n;state=joined": 2, - "jaeger_tracer_traces;sampled=n;state=started": 3, - "jaeger_tracer_traces;sampled=y;state=joined": 0, - "jaeger_tracer_traces;sampled=y;state=started": 0 -} ----- -Helidon publishes whatever metrics Jaeger creates. diff --git a/docs/includes/tracing/tracer-jaeger.adoc b/docs/includes/tracing/tracer-jaeger.adoc index 8ca0d9b3532..2d9313341e6 100644 --- a/docs/includes/tracing/tracer-jaeger.adoc +++ b/docs/includes/tracing/tracer-jaeger.adoc @@ -26,6 +26,8 @@ The Jaeger builder is loaded through `ServiceLoader` and configured. You could also use the Jaeger builder directly, though this would create a source-code dependency on the Jaeger tracer. +Since Helidon 3.0.0, we use Jaeger OpenTelemetry Tracing client to +integrate with Jaeger tracer. include::{rootdir}/includes/dependencies.adoc[] @@ -43,76 +45,16 @@ include::{rootdir}/includes/dependencies.adoc[] == Configuring Jaeger -The Jaeger tracer supports the following configuration options: - -[cols="2,2,2,4", role="flex, sm10"] -|=== -|Key |Default value |Builder method |Description - -|service |N/A |serviceName |Name of the service, to distinguish traces crossing service boundaries; - Jaeger is using lower-case only, name will be automatically lower-cased -|protocol |http |collectorProtocol |Protocol of the Jaeger trace collector (`udp`, `http` or `https`), to switch - to agent mode, use `udp` -|host |localhost |collectorHost |Host of the Jaeger trace collector (IP Address, hostname, or FQDN) -|port |14268 |collectorPort |Port of the Jaeger trace collector -|path |/api/traces |collectorPath |Path of the Jaeger trace collector -|token |N/A |token |Authentication token to use (token authentication) -|username |N/A |username |User to authenticate (basic authentication) -|password |N/A |password |Password of the user to authenticate (basic authentication) -|propagation |library default |addPropagation |Propagation type (`jaeger` or `b3`) -|log-spans |library default |logSpans |Whether to log spans (boolean) -|max-queue-size |library default |maxQueueSize |Maximal queue size of the reporter (int) -|flush-interval-ms|library default |flushInterval |Reporter flush interval in milliseconds -|sampler-type |library default |samplerType |Sampler type (`probabilistic`, `ratelimiting`, `remote`) -|sampler-param |library default |samplerParam |Numeric parameter specifying details for the sampler type -|sampler-manager|library default |samplerManager |Host and port of the sampler manager for `remote` type -|enabled |true |enabled |If set to false, tracing would be disabled -|tags |N/A |addTracerTag(String, String) |`String` tags to add to each span -|boolean-tags |N/A |addTracerTag(String, boolean)|`boolean` tags to add to each span -|int-tags |N/A |addTracerTag(String, int) |`int` tags to add to each span -|=== +include::{rootdir}/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc[tag=config,levelOffset=1] The following is an example of a Jaeger configuration, specified in the YAML format. [source,yaml] ---- tracing: service: "helidon-full-http" - protocol: "https" # JAEGER_ENDPOINT (if not udp, http is expected and endpoint is filled) - host: "192.168.1.3" # JAEGER_ENDPOINT - port: 14240 # JAEGER_ENDPOINT - path: "/api/traces/mine" # JAEGER_ENDPOINT - token: "token" # JAEGER_AUTH_TOKEN - # Either token or username/password - #username: "user" # JAEGER_USER - #password: "pass" # JAEGER_PASSWORD - propagation: "jaeger" # JAEGER_PROPAGATION either "jaeger" or "b3" - log-spans: false # JAEGER_REPORTER_LOG_SPANS - max-queue-size: 42 # JAEGER_REPORTER_MAX_QUEUE_SIZE - flush-interval-ms: 10001 # JAEGER_REPORTER_FLUSH_INTERVAL - sampler-type: "remote"# JAEGER_SAMPLER_TYPE (https://www.jaegertracing.io/docs/latest/sampling/#client-sampling-configuration) - sampler-param: 0.5 # JAEGER_SAMPLER_PARAM (number) - sampler-manager: "localhost:47877" # JAEGER_SAMPLER_MANAGER_HOST_PORT - tags: - tag1: "tag1-value" # JAEGER_TAGS - tag2: "tag2-value" # JAEGER_TAGS - boolean-tags: - tag3: true # JAEGER_TAGS - tag4: false # JAEGER_TAGS - int-tags: - tag5: 145 # JAEGER_TAGS - tag6: 741 # JAEGER_TAGS + protocol: "https" + host: "jaeger" + port: 14240 ---- // end::jaeger-configuration[] - -== Integrating with Jaeger Tracing - -ifdef::se-flavor[] -:jaeger-metrics-page: {rootdir}/se/tracing/jaeger-metrics.adoc -endif::[] -ifdef::mp-flavor[] -:jaeger-metrics-page: {rootdir}/tracing.adoc#jaeger-tracing-metrics -endif::[] - -Jaeger tracks its own behavior using metrics. See xref:{jaeger-metrics-page}[Metrics Support for Jaeger] to read - how to integrate Jaeger metrics with Helidon. diff --git a/docs/includes/tracing/tracer-zipkin.adoc b/docs/includes/tracing/tracer-zipkin.adoc index d781c3e8e9a..7ddfd33e72c 100644 --- a/docs/includes/tracing/tracer-zipkin.adoc +++ b/docs/includes/tracing/tracer-zipkin.adoc @@ -41,26 +41,7 @@ include::{rootdir}/includes/dependencies.adoc[] == Configuring Zipkin -The Zipkin tracer supports the following configuration options: - -[cols="2,2,2,4", role="flex, sm10"] -|=== -|Key |Default value |Builder method |Description - -|service |N/A |serviceName |Name of the service, to distinguish traces crossing service boundaries; - Zipkin is using lower-case only, name will be automatically lower-cased -|protocol |http |collectorProtocol |Protocol of the Zipkin trace collector (http or https) -|host |localhost |collectorHost |Host of the Zipkin trace collector (IP Address, hostname, or FQDN) -|port |9411 |collectorPort |Port of the Zipkin trace collector -|path |defined by version |collectorPath |Path of the Zipkin trace collector, each version uses a different path - by default. -|api-version |2 |version |Zipkin specific method, set the protocol version to communicate with - trace collector -|enabled |true |enabled |If set to false, tracing would be disabled -|tags |N/A |addTracerTag(String, String) |`String` tags to add to each span -|boolean-tags |N/A |addTracerTag(String, boolean)|`boolean` tags to add to each span -|int-tags |N/A |addTracerTag(String, int) |`int` tags to add to each span -|=== +include::{rootdir}/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc[tag=config,levelOffset=+1] The following is an example of a Zipkin configuration, specified in the YAML format. [source,yaml] @@ -69,7 +50,7 @@ tracing: zipkin: service: "helidon-service" protocol: "https" - host: "192.168.1.1" + host: "zipkin" port: 9987 api-version: 1 # this is the default path for API version 2 diff --git a/docs/mp/tracing.adoc b/docs/mp/tracing.adoc index cf9e5099228..fe509ab3ff7 100644 --- a/docs/mp/tracing.adoc +++ b/docs/mp/tracing.adoc @@ -33,17 +33,17 @@ include::{rootdir}/includes/mp.adoc[] - <> - <> - <> -** <> ** <> +** <> - <> == Overview Distributed tracing is a critical feature of micro-service based applications, since it traces workflow both -within a service and across multiple services. This provides insight to sequence and timing data for specific blocks of work, -which helps you identify performance and operational issues. Helidon MP includes support for distributed tracing -through the https://opentracing.io[OpenTracing API]. Tracing is integrated with WebServer, gRPC Server, -and Security using either the https://zipkin.io[Zipkin] or https://www.jaegertracing.io[Jaeger] tracers. +within a service and across multiple services. This provides insight to sequence and timing data for specific blocks of work, +which helps you identify performance and operational issues. Helidon MP includes support for distributed tracing +through the https://opentracing.io[OpenTracing API]. Tracing is integrated with WebServer, gRPC Server, +and Security. include::{rootdir}/includes/dependencies.adoc[] @@ -71,7 +71,8 @@ if you trace a service endpoint that calls another service, then the trace would Within a trace, spans are organized as a directed acyclic graph (DAG) and can belong to multiple services, running on multiple hosts. The _OpenTracing Data Model_ describes the details at https://opentracing.io/specification[The OpenTracing Semantic Specification]. -Spans are automatically created by Helidon as needed during execution of the REST request. +Spans are automatically created by Helidon as needed during execution of the REST request. Additional spans can be added +through MP annotation `@Traced` or through OpenTracing APIs. include::{rootdir}/includes/tracing/common-spans.adoc[] @@ -83,15 +84,7 @@ You can configure a custom service name using the `tracing.service` configuratio property is undefined, name is created from JAX-RS Application name, or `Helidon MP` is used if no application is defined. -All tracer specific configuration is expected in configuration under key `tracing`. - -[source,properties] -.Example `microprofile-config.properties` with customized service name. ----- -tracing.service=event-service ----- - -include::{rootdir}/includes/tracing/common-config.adoc[] +include::{rootdir}/config/io_helidon_tracing_Tracer.adoc[tag=config,levelOffset=2] To disable Helidon tracing for web server and security: [source,properties] @@ -106,25 +99,26 @@ To disables MP Tracing as by specification: mp.opentracing.server.skip-pattern=.* ---- -Tracing configuration can be defined in `microprofile-config.properties` file. - -[source,properties] -.Configuration properties example: ----- -tracing.paths.0.path="/favicon.ico" -tracing.paths.0.enabled=false -tracing.paths.1.path="/metrics" -tracing.paths.1.enabled=false -tracing.paths.2.path="/health" -tracing.paths.2.enabled=false ----- +Tracing configuration can be defined in `application.yaml` file. -[source, properties] -.Tracing configuration example: ----- -tracing.components.web-server.spans.0.name="HTTP Request" -tracing.components.web-server.spans.0.logs.0.name="content-write" -tracing.components.web-server.spans.0.logs.0.enabled=false +[source,yaml] +.Tracing configuration example +---- +tracing: + paths: + - path: "/favicon.ico" + enabled: false + - path: "/metrics" + enabled: false + - path: "/health" + enabled: false + components: + web-server: + spans: + - name: "HTTP Request" + logs: + - name: "content-write" + enabled: false ---- === Controlling Tracing Output @@ -576,7 +570,6 @@ tracing.service=helidon-mp-2 # Microprofile server properties server.port=8081 -server.host=0.0.0.0 ---- [source,bash] @@ -638,7 +631,6 @@ public class GreetResource { this.greetingProvider = greetingConfig; } - @SuppressWarnings("checkstyle:designforextension") @GET @Produces(MediaType.APPLICATION_JSON) public JsonObject getDefaultMessage() { @@ -698,9 +690,10 @@ You can now stop your second service, it is no longer used in this guide. The following example demonstrate how to use Zipkin from a Helidon application running in Kubernetes. [source,bash] -.Add the following line to `META-INF/microprofile-config.properties`: +.Update `application.yaml`: ---- -tracing.host=zipkin +tracing: + host: "zipkin" ---- [source,bash] @@ -906,69 +899,6 @@ include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-configuration] As the <> section describes, you can use Jaeger tracing in your Helidon application. -Jaeger maintains several metrics about its own activity (briefly outlined in the - link:{jaeger-doc-base-url}}/monitoring/#metrics[Jaeger client documentation]). This document explains how you can - integrate those Jaeger tracing metrics with Helidon's metrics. - -=== Prerequisites -Your `pom.xml` file should already contain the dependency for Helidon-Jaeger tracing integration. - -To enable integration with Jaeger's metrics, add the following dependency: - -[source,xml,subs="verbatim,attributes"] ----- - - io.helidon.metrics - helidon-metrics-jaeger - runtime - ----- - -You can leave your application's Java code unchanged. -By adding this dependency, you instruct Helidon to monitor the Jaeger tracing metrics internally and to publish them using the Helidon metrics system. - -Rebuild and start your application. - -== Accessing Jaeger Tracing Metrics -Submit a few requests to your application's endpoints. -This causes Jaeger to update its internal metrics. - -Then, when you access your application's metrics endpoint (`/metrics` by default), Helidon displays the updated Jaeger tracing metrics as part of the `vendor` metrics section. - -[source,bash] ----- -curl -H "Accept: application/json" -X GET http://localhost:8080/metrics/vendor ----- -[source,json] -.Partial Helidon Metrics `vendor` Output Showing Jaeger Metrics ----- -{ - "jaeger_tracer_baggage_restrictions_updates;result=err": 0, - "jaeger_tracer_baggage_restrictions_updates;result=ok": 0, - "jaeger_tracer_baggage_truncations": 0, - "jaeger_tracer_baggage_updates;result=err": 0, - "jaeger_tracer_baggage_updates;result=ok": 0, - "jaeger_tracer_finished_spans": 0, - "jaeger_tracer_reporter_queue_length": 0, - "jaeger_tracer_reporter_spans;result=dropped": 0, - "jaeger_tracer_reporter_spans;result=err": 0, - "jaeger_tracer_reporter_spans;result=ok": 0, - "jaeger_tracer_sampler_queries;result=err": 1, - "jaeger_tracer_sampler_queries;result=ok": 0, - "jaeger_tracer_sampler_updates;result=err": 0, - "jaeger_tracer_sampler_updates;result=ok": 0, - "jaeger_tracer_span_context_decoding_errors": 0, - "jaeger_tracer_started_spans;sampled=n": 15, - "jaeger_tracer_started_spans;sampled=y": 0, - "jaeger_tracer_traces;sampled=n;state=joined": 2, - "jaeger_tracer_traces;sampled=n;state=started": 3, - "jaeger_tracer_traces;sampled=y;state=joined": 0, - "jaeger_tracer_traces;sampled=y;state=started": 0 -} ----- -Helidon publishes whatever metrics Jaeger creates. - - == Reference * link:{microprofile-tracing-spec-url}[MicroProfile Opentracing Specification] diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index 1eb4db2615e..2e38711d8c2 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -127,7 +127,7 @@ A tool-chain to handle authentication, authorization and context propagation. //Tracing [CARD] .Tracing -[icon=timeline,link=tracing/tracing.adoc] +[icon=timeline,link=tracing.adoc] -- Profile and monitor your applications across multiple services. -- diff --git a/docs/se/tracing/tracing.adoc b/docs/se/tracing.adoc similarity index 60% rename from docs/se/tracing/tracing.adoc rename to docs/se/tracing.adoc index a216acde72d..3441d653aae 100644 --- a/docs/se/tracing/tracing.adoc +++ b/docs/se/tracing.adoc @@ -17,12 +17,35 @@ /////////////////////////////////////////////////////////////////////////////// = Tracing -:description: Helidon Tracing Support -:feature-name: Tracing +:description: Helidon SE Tracing Support +:feature-name: Helidon Tracing :keywords: helidon, tracing -:rootdir: {docdir}/../.. +:rootdir: {docdir}/.. include::{rootdir}/includes/se.adoc[] + +== ToC + +- <> +- <> +- <> +- <> +- <> +** <> +** <> +** <> +- <> + +== Overview + +Distributed tracing is a critical feature of micro-service based applications, since it traces workflow both +within a service and across multiple services. This provides insight to sequence and timing data for specific blocks of work, +which helps you identify performance and operational issues. +Helidon includes support for distributed tracing through its own API, backed by either +through the https://opentelemetry.io/docs/instrumentation/js/api/tracing/[OpenTelemetry API], or by +https://opentracing.io[OpenTracing API]. Tracing is integrated with WebServer, gRPC Server, +and Security. + include::{rootdir}/includes/dependencies.adoc[] // tag::tracing-dependency[] @@ -35,19 +58,29 @@ include::{rootdir}/includes/dependencies.adoc[] ---- // end::tracing-dependency[] -== Tracing Support -Helidon includes support for tracing through the https://opentracing.io/[OpenTracing] APIs. -Tracing is integrated with WebServer, gRPC Server, and Security. +== Usage + +This section explains a few concepts that you need to understand before you get started with tracing. + +* In the context of this document, a _service_ is synonymous with an application. +* A _span_ is the basic unit of work done within a single service, on a single host. +Every span has a name, starting timestamp, and duration. +For example, the work done by a REST endpoint is a span. +A span is associated to a single service, but its descendants can belong to different services and hosts. +* A _trace_ contains a collection of spans from one or more services, running on one or more hosts. For example, +if you trace a service endpoint that calls another service, then the trace would contain spans from both services. +Within a trace, spans are organized as a directed acyclic graph (DAG) and +can belong to multiple services, running on multiple hosts. Support for specific tracers is abstracted. Your application can depend on -the abstraction layer and provide a specific tracer implementation as a Java +the Helidon abstraction layer and provide a specific tracer implementation as a Java `ServiceLoader` service. +Helidon provides such an implementation for: -== Configuring Tracing with Helidon SE - -=== Configuring Tracing with WebServer +- OpenTracing tracers, either using the `GlobalTracer`, provider resolver approach, or explicitly using Zipkin tracer +- OpenTelemetry tracers, either using the global OpenTelemetry instance, or explicitly using Jaeger tracer -To configure tracer with WebServer: +=== Setup WebServer [source,java] .Configuring OpenTracing `Tracer` @@ -61,12 +94,12 @@ ServerConfiguration.builder() <1> The name of the application (service) to associate with the tracing events <2> The endpoint for tracing events, specific to the tracer used, usually loaded from Config -=== Configuring Tracing with gRPC Server +=== Setup gRPC Server [source,java] -.Configuring OpenTracing `Tracer` +.Configuring `Tracer` ---- -Tracer tracer = (Tracer) TracerBuilder.create("Server") +Tracer tracer = TracerBuilder.create("Server") .collectorUri(URI.create("http://10.0.0.18:9411")) // <1> .build(); ---- @@ -93,9 +126,7 @@ GrpcServerConfiguration serverConfig = GrpcServerConfiguration.builder().port(0) .build(); ---- -include::{rootdir}/includes/tracing/common-config.adoc[] - -== Creating custom spans +=== Creating custom spans To create a custom span that is a child of the WebServer request: @@ -108,19 +139,15 @@ Span span = request.tracer() ---- -== Trace propagation across services +=== Helidon Spans -Automated trace propagation is supported currently only with Jersey client. +include::{rootdir}/includes/tracing/common-spans.adoc[] -[source,java] -.Tracing propagation with Jersey client ----- -Response response = client.target(serviceEndpoint) - .request() - .get(); ----- +== Configuration -include::{rootdir}/includes/tracing/common-spans.adoc[] +The following configuration should be supported by all tracer implementations (if feasible) + +include::{rootdir}/config/io_helidon_tracing_Tracer.adoc[tag=config,levelOffset=1] === Traced spans configuration @@ -246,3 +273,56 @@ Parameters provided: 1. Method - HTTP method 2. Path - path of the request (such as '/greet') 3. Query - query of the request (may be null) + +== Additional Information + +=== Span Propagation + +Span propagation is supported with Helidon WebClient (and with Jersey client, though it is blocking and not suitable for +reactive implementations). +Tracing propagation is automatic as long as the current span context is available in Helidon Context +(which is automatic when running within a WebServer request). + +[source,xml] +---- + + io.helidon.webclient + helidon-webclient + + + io.helidon.webclient + helidon-webclient-tracing + + +---- + +[source,java] +.Tracing propagation with Helidon WebClient +---- +WebClient client = WebClient.builder() + .addService(WebClientTracing.create()) + .build(); + +Single response = client.get() + .uri(uri) + .request(String.class); +---- + +=== Zipkin Tracing [[zipkin-tracing]] + +include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-dependency] +include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-configuration] + +=== Jaeger Tracing [[jaeger-tracing]] + +include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-dependency] +include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-configuration] + +=== Jaeger Tracing Metrics [[jaeger-tracing-metrics]] + +As the <> section describes, you can use Jaeger tracing in your Helidon application. + +== Reference + +* link:https://opentracing.io/[Opentracing Project] +* link:https://opentelemetry.io/docs/instrumentation/js/api/tracing/[OpenTelemetry API] diff --git a/docs/se/tracing/jaeger-metrics.adoc b/docs/se/tracing/jaeger-metrics.adoc deleted file mode 100644 index 19dee1e475a..00000000000 --- a/docs/se/tracing/jaeger-metrics.adoc +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Metrics Support for Jaeger -:description: Helidon support for Jaeger metrics -:keywords: helidon, metrics, tracing, jaeger -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] -include::{rootdir}/includes/tracing/jaeger-metrics.adoc[] diff --git a/docs/se/tracing/jaeger.adoc b/docs/se/tracing/jaeger.adoc deleted file mode 100644 index 28bddea90f6..00000000000 --- a/docs/se/tracing/jaeger.adoc +++ /dev/null @@ -1,26 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Jaeger Tracing -:description: Helidon Tracing Support -:keywords: helidon, tracing, jaeger -:feature-name: Jaeger Tracing -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] -include::{rootdir}/includes/tracing/tracer-jaeger.adoc[] diff --git a/docs/se/tracing/zipkin.adoc b/docs/se/tracing/zipkin.adoc deleted file mode 100644 index 22484ca5eb2..00000000000 --- a/docs/se/tracing/zipkin.adoc +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Zipkin Tracing -:description: Helidon Tracing Support -:keywords: helidon, tracing, zipkin -:feature-name: Zipkin Tracing -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/tracing/tracer-zipkin.adoc[] diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index a576e4cff0e..e9f560fb681 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -238,17 +238,12 @@ backend: glyph: type: "icon" value: "access_alarm" - - type: "MENU" + - type: "PAGE" title: "Tracing" - dir: "tracing" + source: "tracing.adoc" glyph: type: "icon" value: "timeline" - sources: - - "tracing.adoc" - - "zipkin.adoc" - - "jaeger.adoc" - - "jaeger-metrics.adoc" - type: "PAGE" title: "Vault" source: "vault.adoc" diff --git a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java index bcb3b4e7b7a..8655d15690a 100644 --- a/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java +++ b/tracing/jaeger/src/main/java/io/helidon/tracing/jaeger/JaegerTracerBuilder.java @@ -23,6 +23,7 @@ import io.helidon.config.Config; import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.tracing.Tracer; import io.helidon.tracing.TracerBuilder; import io.helidon.tracing.opentelemetry.HelidonOpenTelemetry; @@ -288,6 +289,7 @@ public JaegerTracerBuilder config(Config config) { * @param resource key resource * @return updated builder */ + @ConfiguredOption(key = "private-key-pem") public JaegerTracerBuilder privateKey(io.helidon.common.configurable.Resource resource) { this.privateKey = resource.bytes(); return this; @@ -299,6 +301,7 @@ public JaegerTracerBuilder privateKey(io.helidon.common.configurable.Resource re * @param resource certificate resource * @return updated builder */ + @ConfiguredOption(key = "client-cert-pem") public JaegerTracerBuilder clientCertificate(io.helidon.common.configurable.Resource resource) { this.certificate = resource.bytes(); return this; @@ -310,6 +313,7 @@ public JaegerTracerBuilder clientCertificate(io.helidon.common.configurable.Reso * @param resource trusted certificates resource * @return updated builder */ + @ConfiguredOption(key = "trusted-cert-pem") public JaegerTracerBuilder trustedCertificates(io.helidon.common.configurable.Resource resource) { this.trustedCertificates = resource.bytes(); return this; @@ -321,6 +325,7 @@ public JaegerTracerBuilder trustedCertificates(io.helidon.common.configurable.Re * @param exporterTimeout timeout to use * @return updated builder */ + @ConfiguredOption(key = "exporter-timeout-millis", value = "10000") public JaegerTracerBuilder exporterTimeout(Duration exporterTimeout) { this.exporterTimeout = exporterTimeout; return this; @@ -353,6 +358,7 @@ public B unwrap(Class builderClass) { * @param samplerParam parameter of the sampler * @return updated builder instance */ + @ConfiguredOption("1") public JaegerTracerBuilder samplerParam(Number samplerParam) { this.samplerParam = samplerParam; return this; @@ -366,6 +372,7 @@ public JaegerTracerBuilder samplerParam(Number samplerParam) { * @param samplerType type of the sampler * @return updated builder instance */ + @ConfiguredOption("CONSTANT") public JaegerTracerBuilder samplerType(SamplerType samplerType) { this.samplerType = samplerType; return this; diff --git a/tracing/opentracing/pom.xml b/tracing/opentracing/pom.xml index 166b3c34147..33ffeeb92d5 100644 --- a/tracing/opentracing/pom.xml +++ b/tracing/opentracing/pom.xml @@ -55,6 +55,12 @@ io.helidon.common helidon-common-service-loader + + io.helidon.config + helidon-config-metadata + provided + true + org.junit.jupiter junit-jupiter-api @@ -71,4 +77,22 @@ test + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.config + helidon-config-metadata-processor + ${helidon.version} + + + + + + diff --git a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerBuilder.java b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerBuilder.java index b1a8cad1006..dc276f6fb5f 100644 --- a/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerBuilder.java +++ b/tracing/opentracing/src/main/java/io/helidon/tracing/opentracing/OpenTracingTracerBuilder.java @@ -20,6 +20,8 @@ import io.helidon.common.Builder; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.opentracing.Tracer; @@ -98,6 +100,7 @@ * are of the correct type and contain all methods, even those not inherited from this * interface */ +@Configured(description = "OpenTracing tracer configuration.", ignoreBuildMethod = true) public interface OpenTracingTracerBuilder> extends Builder { /** * Create a new builder for the service name. @@ -127,6 +130,7 @@ static OpenTracingTracerBuilder create(Config config) { * @param name name of the service using the tracer * @return updated builder instance */ + @ConfiguredOption(key = "service") T serviceName(String name); /** @@ -171,6 +175,7 @@ default T collectorUri(URI uri) { * @param protocol protocol to use * @return updated builder instance */ + @ConfiguredOption(key = "protocol") T collectorProtocol(String protocol); /** @@ -180,6 +185,7 @@ default T collectorUri(URI uri) { * @param port port to use * @return updated builder instance */ + @ConfiguredOption(key = "port") T collectorPort(int port); /** @@ -189,6 +195,7 @@ default T collectorUri(URI uri) { * @param host host to use * @return updated builder instance */ + @ConfiguredOption(key = "host") T collectorHost(String host); /** @@ -198,6 +205,7 @@ default T collectorUri(URI uri) { * @param path path to use * @return updated builder instance */ + @ConfiguredOption(key = "path") T collectorPath(String path); /** @@ -207,6 +215,7 @@ default T collectorUri(URI uri) { * @param value value of the tag * @return updated builder instance */ + @ConfiguredOption(key = "tags", kind = ConfiguredOption.Kind.MAP, type = String.class) T addTracerTag(String key, String value); /** @@ -216,6 +225,7 @@ default T collectorUri(URI uri) { * @param value numeric value of the tag * @return updated builder instance */ + @ConfiguredOption(key = "int-tags", kind = ConfiguredOption.Kind.MAP, type = Integer.class) T addTracerTag(String key, Number value); /** @@ -225,6 +235,7 @@ default T collectorUri(URI uri) { * @param value boolean value of the tag * @return updated builder instance */ + @ConfiguredOption(key = "boolean-tags", kind = ConfiguredOption.Kind.MAP, type = Boolean.class) T addTracerTag(String key, boolean value); /** @@ -244,6 +255,7 @@ default T collectorUri(URI uri) { * @param enabled set to {@code false} to disable distributed tracing * @return updated builder instance */ + @ConfiguredOption("true") T enabled(boolean enabled); /** @@ -252,6 +264,7 @@ default T collectorUri(URI uri) { * @param global whether to register this tracer as a global tracer once built * @return updated builder instance */ + @ConfiguredOption(key = "global", value = "true") T registerGlobal(boolean global); /** diff --git a/tracing/opentracing/src/main/java/module-info.java b/tracing/opentracing/src/main/java/module-info.java index b51632cf8ce..d01fe5e4151 100644 --- a/tracing/opentracing/src/main/java/module-info.java +++ b/tracing/opentracing/src/main/java/module-info.java @@ -22,6 +22,7 @@ requires transitive io.helidon.common; requires transitive io.helidon.config; requires transitive io.helidon.tracing; + requires static io.helidon.config.metadata; requires io.opentracing.util; requires io.opentracing.api; diff --git a/tracing/tracing/src/main/java/io/helidon/tracing/TracerBuilder.java b/tracing/tracing/src/main/java/io/helidon/tracing/TracerBuilder.java index 3af4ac602d8..e9a95bdaa47 100644 --- a/tracing/tracing/src/main/java/io/helidon/tracing/TracerBuilder.java +++ b/tracing/tracing/src/main/java/io/helidon/tracing/TracerBuilder.java @@ -98,7 +98,7 @@ * are of the correct type and contain all methods, even those not inherited from this * interface */ -@Configured(description = "OpenTracing tracer configuration.", ignoreBuildMethod = true) +@Configured(description = "Tracer configuration.", ignoreBuildMethod = true) public interface TracerBuilder> extends Builder { /** * Create a new builder for the service name. diff --git a/tracing/zipkin/pom.xml b/tracing/zipkin/pom.xml index 05c8dfbcd36..e29764e715b 100644 --- a/tracing/zipkin/pom.xml +++ b/tracing/zipkin/pom.xml @@ -57,6 +57,12 @@ io.opentracing.brave brave-opentracing + + io.helidon.config + helidon-config-metadata + provided + true + org.junit.jupiter @@ -84,4 +90,22 @@ test + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.config + helidon-config-metadata-processor + ${helidon.version} + + + + + + diff --git a/tracing/zipkin/src/main/java/io/helidon/tracing/zipkin/ZipkinTracerBuilder.java b/tracing/zipkin/src/main/java/io/helidon/tracing/zipkin/ZipkinTracerBuilder.java index 5ea8b17db3b..30cff7718cd 100644 --- a/tracing/zipkin/src/main/java/io/helidon/tracing/zipkin/ZipkinTracerBuilder.java +++ b/tracing/zipkin/src/main/java/io/helidon/tracing/zipkin/ZipkinTracerBuilder.java @@ -25,6 +25,8 @@ import java.util.logging.Logger; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.tracing.Tag; import io.helidon.tracing.TracerBuilder; import io.helidon.tracing.opentracing.OpenTracingTracerBuilder; @@ -106,6 +108,7 @@ * @see Zipkin Attributes * @see Zipkin Missing Service Name */ +@Configured(prefix = "tracing", root = true, description = "Zipkin tracer configuration") public class ZipkinTracerBuilder implements OpenTracingTracerBuilder { static final Logger LOGGER = Logger.getLogger(ZipkinTracerBuilder.class.getName()); static final String DEFAULT_PROTOCOL = "http"; @@ -342,6 +345,7 @@ public Tracer build() { * @param version version to use * @return updated builder instance */ + @ConfiguredOption(value = "V2", key = "api-version") public ZipkinTracerBuilder version(Version version) { this.version = version; return this; diff --git a/tracing/zipkin/src/main/java/module-info.java b/tracing/zipkin/src/main/java/module-info.java index 59a0153372b..42c58da9bfd 100644 --- a/tracing/zipkin/src/main/java/module-info.java +++ b/tracing/zipkin/src/main/java/module-info.java @@ -22,6 +22,7 @@ requires io.helidon.config; requires io.helidon.tracing; requires io.helidon.tracing.opentracing; + requires static io.helidon.config.metadata; requires java.logging; requires io.opentracing.util; From 52cb00d74e175193babd7f946c5e9633d3b691c3 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Fri, 15 Jul 2022 18:01:00 +0300 Subject: [PATCH 26/51] [New Doc PR] - Helidon MP JWT Auth #4200 (#4515) * Rebased to new config * Applied comments --- docs/config/io_helidon_microprofile_jwt.adoc | 80 ++++++++++++ docs/mp/jwt.adoc | 121 ++++++++++++++++++- 2 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 docs/config/io_helidon_microprofile_jwt.adoc diff --git a/docs/config/io_helidon_microprofile_jwt.adoc b/docs/config/io_helidon_microprofile_jwt.adoc new file mode 100644 index 00000000000..88cfe9908c6 --- /dev/null +++ b/docs/config/io_helidon_microprofile_jwt.adoc @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +// MANUALLY CREATED DOC + +:description: Configuration of io.helidon.microprofile.jwt +:keywords: helidon, config, io.helidon.health.HealthSupport.adoc +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.microprofile.jwt.adoc + += JWT Configuration + +// tag::config[] + +== Configuration options + + +MicroProfile configuration options: +[cols="3,3,2,5"] + +|=== +|key |type |default value |description + +|`mp.jwt.verify.publickey` |string |{nbsp} |The property allows the Public Verification Key text itself to be supplied as a string. +|`mp.jwt.verify.publickey.location` |string |{nbsp} | The property allows for an external or internal location of Public Verification Key to be specified. The value may be a relative path or a URL. +|`mp.jwt.verify.publickey.algorithm` |string |{nbsp} |The configuration property allows for specifying which Public Key Signature Algorithm is supported by the MP JWT endpoint. This property can be set to either `RS256` or `ES256`. Default value is `RS256`. Support for the other asymmetric signature algorithms such as `RS512`, `ES512` and others is optional. + +|=== + +Optional configuration options: +[cols="3,3,2,5"] + +|=== +|key |type |default value |description + +|`optional` |boolean |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is +an important distinction when more than one provider is used +|`authenticate` |boolean |`true` |Whether to attempt authentication +|`propagate`|boolean |`true` |Whether to attempt identity propagation/JWT creation +|`principal-type`|string |`USER` |Whether we authenticate a user or a service (other option is SERVICE) +|`atn-token` |string |A group for configuring authentication of the request +|`atn-token.verify-signature`|boolean |`true` |Whether to verify signature in incoming JWT. If disabled, _ANY_ JWT will be accepted +|`atn-token.jwt-audience`|string |{nbsp} |Expected audience of the JWT. If not defined, any audience is accepted (and we may accept JWT not inteded for us) +|`atn-token.jwk.resource.*`|string |{nbsp} |Configuration of the JWK to obtain key(s) to validate signatures of inbound token. The JWK should contain public keys. This may be: jwk.resource.path, jwk.resource.resource-path, jwk.resource.url, jwk.resource.content-plain (actual JSON string), jwk.resource.content (base64) +|`atn-token.handler`|string |`Authorization` header with `bearer ` prefix |A handler configuration for inbound token - e.g. how to extract it +|`atn-token.handler.header`|string |{nbsp} |Name of a header the token is expected in +|`atn-token.handler.prefix`|string |{nbsp} |Prefix before the token value (optional) +|`atn-token.handler.regexp`|string |{nbsp} |Regular expression to obtain the token, first matching group is used (optional) +|`sign-token`|string |{nbsp} |A group for configuring outbound security +|`sign-token.jwk.resource.*` |{nbsp} |Configuration of the JWK to use when generating tokens (follows same rules as atn-token.jwk above), this JWK must contain private keys when using asymmetric ciphers +|`sign-token.jwt-issuer`|string |{nbsp} |When we issue a new token, this is the issuer to be placed into it (validated by target service) +|`sign-token.outbound`|string |{nbsp} |A group for configuring outbound rules (based on transport, host and.or path) +|`sign-token.outbound.*.name`|string |{nbsp} |A short descriptive name for configured target service(s) +|`sign-token.outbound.*.transports`|string |any |An array of transports this outbound matches (e.g. https) +|`sign-token.outbound.*.hosts`|string |any |An array of hosts this outbound matches, may use * as a wild-card (e.g. *.oracle.com) +|`sign-token.outbound.*.paths`|string |any |An array of paths on the host this outbound matches, may use * as a wild-card (e.g. /some/path/*) +|`sign-token.outbound.*.outbound-token`|string |`Authorization` header with `bearer ` prefix |Configuration of outbound token handler (same as atn-token.handler) +|`sign-token.outbound.*.outbound-token.format`|string |{nbsp} |Java text format for generating the value of outbound token header (e.g. "bearer %1$s") +|`sign-token.outbound.*.jwk-kid`|string |{nbsp} |If this key is defined, we are generating a new token, otherwise we propagate existing. Defines the key id of a key definition in the JWK file to use for signing the outbound token +|`sign-token.outbound.*.jwt-kid`|string |{nbsp} |A key to use in the generated JWT - this is for the other service to locate the verification key in their JWK +|`sign-token.outbound.*.jwt-audience`|string |{nbsp} |Audience this key is generated for (e.g. http://www.example.org/api/myService) - validated by the other service +|`sign-token.outbound.*.jwt-not-before-seconds`|string |`5` |Makes this key valid this amount of seconds into the past. Allows a certain time-skew for the generated token to be valid before current time (e.g. when we expect a certain misalignment of clocks) +|`sign-token.outbound.*.jwt-validity-seconds`|string |1 day |Token validity in seconds +|=== + +// end::config[] diff --git a/docs/mp/jwt.adoc b/docs/mp/jwt.adoc index c1e11ad09a2..6e949a5d81b 100644 --- a/docs/mp/jwt.adoc +++ b/docs/mp/jwt.adoc @@ -27,6 +27,24 @@ :rootdir: {docdir}/.. include::{rootdir}/includes/mp.adoc[] +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +JSON Web Tokens (JWT) are an open, industry standard https://datatracker.ietf.org/doc/html/rfc7519[(RFC 7519)] method for representing claims securely between two parties. + +JSON Web Token defines a compact and self-contained way for securely transmitting information between parties as a JSON object. With JWT Auth you can integrate security features such as single sign on into your Helidon MP applications. + + include::{rootdir}/includes/dependencies.adoc[] [source,xml] @@ -37,15 +55,12 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Overview -JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object. With JWT Auth you can integrate security features such as single sign on into your Helidon MP applications. - -== Next Steps +== Usage The main configuration point for JWT Auth is a JAX-RS Application class. As this class is discovered using CDI, it must have a bean defining annotation. -Example of an application that enables JWT-Auth (with minimal required annotations): +Minimal required setup is done using `@LoginConfig(authMethod = "MP-JWT")`: [source,java] ---- @@ -55,6 +70,100 @@ public class ProtectedApplication extends Application{ } ---- -Learn more about JWT authentication: + +== API + +The following interfaces and annotations are used to work with JWT in Helidon MP: + +* `JsonWebToken` - an interface used in CDI beans _(@RequestScoped)_ dependency injection to obtain the JWT of the currently executing caller. +* `@Claim` - an annotation used by CDI bean _(@RequestScoped)_ dependency injection to obtain individual claims from the caller’s JWT. +* `ClaimValue` - a proxy interface used with `@Claim` annotation to оbtain the value of a claim by calling `getValue()`. + +== Configuration + +include::{rootdir}/config/io_helidon_microprofile_jwt.adoc[leveloffset=+1,tag=config] + +A configuration example in `microprofile-config.properties`: +[source, properties] +---- +mp.jwt.verify.issuer=https://{PublicIssuerDomain}/oauth2/default +mp.jwt.verify.publickey.location=${mp.jwt.verify.issuer}/v1/keys +---- + +== Examples + +[source, java] +---- +@Path("/hello") +public class HelloResource { + + @GET + @Produces(TEXT_PLAIN) + public String hello(@Context SecurityContext context) { + Optional userPrincipal = context.userPrincipal(); + return "Hello, " + userPrincipal.get().getName() + "!"; + } +} +---- + +Do not forget to annotate the `HelloApplication` class to enable JWT: + +[source, java] +---- +@LoginConfig(authMethod = "MP-JWT") +@ApplicationScoped +public class HelloApplication extends Application { + @Override + public Set> getClasses() { + return Set.of(HelloResource.class); + } +} +---- + +Add the following configuration in `microprofile-config.properties`: + +[source, properties] +---- +mp.jwt.verify.issuer=https://{IssuerPublicDomain}/oauth2/default +mp.jwt.verify.publickey.location=${mp.jwt.verify.issuer}/v1/keys +---- + +Obtain the Security Token from external issuer: + +[source, bash] +---- +TOKEN=sdf4dDSWFcswdsffDSasEgv... +---- + +Run the application and execute an http request against it: + +[source, bash] +---- +curl -X GET -I -H "Authorization: Bearer $TOKEN" http://localhost:8080/hello + +---- + +The result should be: + +[source, bash] +---- +HTTP/1.1 200 OK +Date: 08.06.2022 10:33:47 EEST +connection: keep-alive +content-length: 28 + +Hello, secure@helidon.io! +---- + +which means that the request successfully passed authentication. + +== Additional Information + +Learn more about JWT authentication at: + link:{microprofile-jwt-base-url}#_introduction[Eclipse MicroProfile Interoperable JWT RBAC] + +== Reference + +* {microprofile-jwt-spec-url}[MicroProfile JWT Auth Spec] +* https://github.com/eclipse/microprofile-jwt-auth[MicroProfile JWT Auth GitHub Repository] + From 7479757b728f8555a83785c1441f0822b84a0879 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Fri, 15 Jul 2022 18:01:23 +0300 Subject: [PATCH 27/51] [New Doc PR] JUnit5 Testing #4300 (#4505) * JUnit5 Testing New style doc * Minor refactoring. TestNG added. * Minor guide reference fix * Fix sitegen * Fix comments --- docs/mp/guides/testing-junit5.adoc | 2 +- docs/mp/{testing => }/testing-ng.adoc | 106 ++++++++++++++-------- docs/mp/{testing => }/testing.adoc | 124 ++++++++++++++++---------- docs/sitegen.yaml | 1 - 4 files changed, 149 insertions(+), 84 deletions(-) rename docs/mp/{testing => }/testing-ng.adoc (66%) rename docs/mp/{testing => }/testing.adoc (69%) diff --git a/docs/mp/guides/testing-junit5.adoc b/docs/mp/guides/testing-junit5.adoc index d609123f7f2..90e69caced6 100644 --- a/docs/mp/guides/testing-junit5.adoc +++ b/docs/mp/guides/testing-junit5.adoc @@ -261,7 +261,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/testing.adoc[Testing with JUnit 5] +* xref:../testing.adoc[Testing with JUnit 5] diff --git a/docs/mp/testing/testing-ng.adoc b/docs/mp/testing-ng.adoc similarity index 66% rename from docs/mp/testing/testing-ng.adoc rename to docs/mp/testing-ng.adoc index a034455e00b..ff83eed50d6 100644 --- a/docs/mp/testing/testing-ng.adoc +++ b/docs/mp/testing-ng.adoc @@ -20,9 +20,18 @@ :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[] +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview Helidon provides built-in test support for CDI testing in TestNG. @@ -42,6 +51,41 @@ A test can be annotated with `io.helidon.microprofile.tests.testng.HelidonTest` CDI test. This annotation will start the CDI container before any test method is invoked, and stop it after the last method is invoked. This annotation also enables injection into the test class itself. +== Usage + +A test can be annotated with `io.helidon.microprofile.tests.testng.HelidonTest` annotation to mark it as a +CDI test. This annotation will start the CDI container before any test method is invoked, and stop it after +the last method is invoked. This annotation also enables injection into the test class itself. + +=== Usage - per method CDI container +A test can be annotated as follows: + +`@HelidonTest(resetPerTest = true)` + +This will change the behavior as follows: + +- A new CDI container is created for each test method invocation +- annotations to add config, beans and extension can be added for each method in addition to the class +- 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 +`@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 +it is important to set property `mp.initializer.allow=true` in order CDI container to start, when used with +`@HelidonTest`. +You can set up config in `@BeforeAll` method and register it with `ConfigProviderResolver` using MP Config APIs, and declare +`@Configuration(useExisting=true)`. +Note that this is not compatible with repeatable tests that use method sources that access CDI, as we must delay the CDI +startup to the test class instantiation (which is too late, as the method sources are already invoked by this time). + +*If you want to use method sources that use CDI with repeatable tests, please do not use `@Configuration(useExisting=true)`* + +NOTE: Test method parameters are currently not supported. + +== API + The annotations described in this section are inherited (for the non-repeatable ones), and additive (for repeatable). So if you declare `@DisableDiscovery` on abstract class, all implementations will have discovery disabled, unless you annotate the implementation class with `@DisableDiscovery(false)`. @@ -49,13 +93,26 @@ If you declare `@AddBean` on both abstract class and implementation class, both In addition to this simplification, the following annotations are supported: -- `io.helidon.microprofile.tests.testng.AddBean` - to add one or more beans to the container - (if not part of a bean archive, or when discovery is disabled) -- `io.helidon.microprofile.tests.testng.AddExtension` - to add one or more CDI extensions to the container - (if not added through service loader, or when discovery is disabled) -- `io.helidon.microprofile.tests.testng.AddConfig` - to add one or more configuration properties to MicroProfile config - without the need of creating a `microprofile-config.properties` file -- `io.helidon.microprofile.tests.testng.DisableDiscovery` - to disable automated discovery of beans and extensions + +|=== +|Annotation | Usage + +|`@io.helidon.microprofile.tests.testng.AddBean` +|Used to add one or more beans to the container (if not part of a bean archive, or when discovery is disabled) + +|`@io.helidon.microprofile.tests.testng.AddExtension` +|Used to add one or more CDI extensions to the container (if not added through service loader, or when discovery is disabled) + +|`@io.helidon.microprofile.tests.testng.AddConfig` +|Used to add one or more configuration properties to MicroProfile config without the need of creating a `microprofile-config.properties` file + +|`@io.helidon.microprofile.tests.testng.DisableDiscovery` +|Used to disable automated discovery of beans and extensions +|=== + +== Examples + +In current example Helidon container will be launched prior test. The _Bean Discovery_ will be disabled. _MyBean_ will be added to the test, so that it can be injected. _ConfigCdiExtension_ will be enabled for this test. And finally, a configuration property will be added using `@AddConfig` annotation. [source,java] .Code sample @@ -65,7 +122,7 @@ In addition to this simplification, the following annotations are supported: @AddBean(MyBean.class) @AddExtension(ConfigCdiExtension.class) @AddConfig(key = "app.greeting", value = "TestHello") -class TestNoDiscovery { +class TestExample { @Inject private MyBean myBean; @@ -77,32 +134,7 @@ class TestNoDiscovery { } ---- -== Usage - per method CDI container -A test can be annotated as follows: - -`@HelidonTest(resetPerTest = true)` - -This will change the behavior as follows: - -- A new CDI container is created for each test method invocation -- annotations to add config, beans and extension can be added for each method in addition to the class -- 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 - `@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 -it is important to set property `mp.initializer.allow=true` in order CDI container to start, when used with -`@HelidonTest`. -You can set up config in `@BeforeAll` method and register it with `ConfigProviderResolver` using MP Config APIs, and declare -`@Configuration(useExisting=true)`. -Note that this is not compatible with repeatable tests that use method sources that access CDI, as we must delay the CDI -startup to the test class instantiation (which is too late, as the method sources are already invoked by this time). - -*If you want to use method sources that use CDI with repeatable tests, please do not use `@Configuration(useExisting=true)`* -== Usage - added parameters and injection types +== Reference -Test method parameters are currently not supported. \ No newline at end of file +* https://testng.org/doc/documentation-main.html[TestNG User Guide] diff --git a/docs/mp/testing/testing.adoc b/docs/mp/testing.adoc similarity index 69% rename from docs/mp/testing/testing.adoc rename to docs/mp/testing.adoc index 84fe570f307..c540f2f1049 100644 --- a/docs/mp/testing/testing.adoc +++ b/docs/mp/testing.adoc @@ -17,12 +17,23 @@ /////////////////////////////////////////////////////////////////////////////// = Testing with JUnit5 -:description: Helidon Testing with JUnit15 +:h1Prefix: MP +:pagename: testing +: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[] +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview Helidon provides built-in test support for CDI testing in JUnit5. @@ -36,11 +47,51 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Usage - default +== Usage A test can be annotated with `io.helidon.microprofile.tests.junit5.HelidonTest` annotation to mark it as a CDI test. This annotation will start the CDI container before any test method is invoked, and stop it after the last method is invoked. This annotation also enables injection into the test class itself. +=== Usage - per method CDI container +A test can be annotated as follows: + +`@HelidonTest(resetPerTest = true)` + +This will change the behavior as follows: + +- A new CDI container is created for each test method invocation +- annotations to add config, beans and extension can be added for each method in addition to the class +- you cannot inject fields or constructor parameters of the test class itself (as a single instance is shared by more containers) +- 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 +`@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 +it is important to set property `mp.initializer.allow=true` in order CDI container to start, when used with +`@HelidonTest`. +You can set up config in `@BeforeAll` method and register it with `ConfigProviderResolver` using MP Config APIs, and declare +`@Configuration(useExisting=true)`. +Note that this is not compatible with repeatable tests that use method sources that access CDI, as we must delay the CDI +startup to the test class instantiation (which is too late, as the method sources are already invoked by this time). + +*If you want to use method sources that use CDI with repeatable tests, please do not use `@Configuration(useExisting=true)`* + +=== Usage - added parameters and injection types +The following types are available for injection (when a single CDI container is used per test class): + +- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on +the classpath + +The following types are available as method parameters (in any type of Helidon tests): + +- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on +the classpath +- `SeContainer` - the current container instance + +== API + The annotations described in this section are inherited (for the non-repeatable ones), and additive (for repeatable). So if you declare `@DisableDiscovery` on abstract class, all implementations will have discovery disabled, unless you annotate the implementation class with `@DisableDiscovery(false)`. @@ -48,13 +99,25 @@ If you declare `@AddBean` on both abstract class and implementation class, both In addition to this simplification, the following annotations are supported: -- `io.helidon.microprofile.tests.junit5.AddBean` - to add one or more beans to the container - (if not part of a bean archive, or when discovery is disabled) -- `io.helidon.microprofile.tests.junit5.AddExtension` - to add one or more CDI extensions to the container - (if not added through service loader, or when discovery is disabled) -- `io.helidon.microprofile.tests.junit5.AddConfig` - to add one or more configuration properties to MicroProfile config - without the need of creating a `microprofile-config.properties` file -- `io.helidon.microprofile.tests.junit5.DisableDiscovery` - to disable automated discovery of beans and extensions +|=== +|Annotation | Usage + +|`@io.helidon.microprofile.tests.junit5.AddBean` +|Used to add one or more beans to the container (if not part of a bean archive, or when discovery is disabled) + +|`@io.helidon.microprofile.tests.junit5.AddExtension` +|Used to add one or more CDI extensions to the container (if not added through service loader, or when discovery is disabled) + +|`@io.helidon.microprofile.tests.junit5.AddConfig` +|Used to add one or more configuration properties to MicroProfile config without the need of creating a `microprofile-config.properties` file + +|Used `@io.helidon.microprofile.tests.junit5.DisableDiscovery` +|to disable automated discovery of beans and extensions +|=== + +== Examples + +In current example Helidon container will be launched prior test. The _Bean Discovery_ will be disabled. _MyBean_ will be added to the test, so that it can be injected. _ConfigCdiExtension_ will be enabled for this test. And finally, a configuration property will be added using `@AddConfig` annotation. [source,java] .Code sample @@ -64,7 +127,7 @@ In addition to this simplification, the following annotations are supported: @AddBean(MyBean.class) @AddExtension(ConfigCdiExtension.class) @AddConfig(key = "app.greeting", value = "TestHello") -class TestNoDiscovery { +class TestExample { @Inject private MyBean myBean; @@ -76,40 +139,11 @@ class TestNoDiscovery { } ---- -== Usage - per method CDI container -A test can be annotated as follows: -`@HelidonTest(resetPerTest = true)` +== Additional Information -This will change the behavior as follows: +* https://medium.com/helidon/testing-helidon-9df2ea14e22[Official blog article about Helidon and JUnit usage] -- A new CDI container is created for each test method invocation -- annotations to add config, beans and extension can be added for each method in addition to the class -- you cannot inject fields or constructor parameters of the test class itself (as a single instance is shared by more containers) -- you can add `SeContainer` as a method parameter of any test method and you will get the current container +== Reference -== Usage - configuration -In addition to the `@AddConfig` annotation, 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 -it is important to set property `mp.initializer.allow=true` in order CDI container to start, when used with -`@HelidonTest`. -You can set up config in `@BeforeAll` method and register it with `ConfigProviderResolver` using MP Config APIs, and declare -`@Configuration(useExisting=true)`. -Note that this is not compatible with repeatable tests that use method sources that access CDI, as we must delay the CDI -startup to the test class instantiation (which is too late, as the method sources are already invoked by this time). - -*If you want to use method sources that use CDI with repeatable tests, please do not use `@Configuration(useExisting=true)`* - -== Usage - added parameters and injection types -The following types are available for injection (when a single CDI container is used per test class): - -- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on - the classpath - -The following types are available as method parameters (in any type of Helidon tests): - -- `WebTarget` - a JAX-RS client's target configured for the current hostname and port when `helidon-micorprofile-server` is on - the classpath -- `SeContainer` - the current container instance \ No newline at end of file +* https://junit.org/junit5/docs/current/user-guide/[JUnit 5 User Guide] diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index e9f560fb681..3a046131c49 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -510,7 +510,6 @@ backend: value: "save" - type: "MENU" title: "Testing" - dir: "testing" glyph: type: "icon" value: "thumbs_up_down" From e1e9505cf4d0b48b1449534d8f845412ca40e0ed Mon Sep 17 00:00:00 2001 From: Dmitry Kornilov Date: Fri, 15 Jul 2022 17:03:42 +0200 Subject: [PATCH 28/51] Reactive Streams Operators for SE and MP (#4520) * Reactive Streams Operators for SE and MP * Reference section added --- docs/about/introduction.adoc | 4 +- docs/includes/reactivestreams/engine.adoc | 44 +++++++++------ .../includes/reactivestreams/rsoperators.adoc | 55 ++++++++++++------- docs/mp/introduction/introduction.adoc | 2 +- docs/mp/reactivestreams/engine.adoc | 2 - docs/mp/reactivestreams/overview.adoc | 49 ----------------- docs/mp/reactivestreams/rsoperators.adoc | 13 ++--- docs/se/introduction.adoc | 2 +- docs/se/reactivestreams/engine.adoc | 5 +- docs/se/reactivestreams/overview.adoc | 49 ----------------- docs/se/reactivestreams/rsoperators.adoc | 4 +- docs/sitegen.yaml | 2 - 12 files changed, 73 insertions(+), 158 deletions(-) delete mode 100644 docs/mp/reactivestreams/overview.adoc delete mode 100644 docs/se/reactivestreams/overview.adoc diff --git a/docs/about/introduction.adoc b/docs/about/introduction.adoc index 58fc40a5675..8dc11812062 100644 --- a/docs/about/introduction.adoc +++ b/docs/about/introduction.adoc @@ -81,17 +81,15 @@ One of the new features in Helidon 2.0 is the addition of a command line interfa The new database client for Helidon SE will include support for the MongoDB reactive driver and brings Health Checks, Metrics, and Tracing support to every Helidon API. xref:../se/dbclient.adoc[Learn more about the DB Client]. - * *Extending MicroProfile Reactive Messaging and Reactive Operators Support* + MP Reactive Operators will be included in both frameworks, while MP Reactive Messaging will only be included in Helidon MP. xref:../mp/reactivemessaging/introduction.adoc[Learn more about Reactive Messaging] and - xref:../mp/reactivestreams/overview.adoc[Reactive Streams]. + xref:../mp/reactivestreams/rsoperators.adoc[Reactive Streams]. * *Helidon Web Client* + The new reactive web client can integrate with other Helidon SE APIs. xref:../se/webclient/introduction.adoc[Learn more about the Helidon Web Client]. - * *Additional Websocket Support* + Based upon the Tyrus implementation, Helidon receives WebSocket API support. xref:../se/websocket.adoc[Learn more about Websocket Support]. diff --git a/docs/includes/reactivestreams/engine.adoc b/docs/includes/reactivestreams/engine.adoc index a8e0f9cbc1d..9e1d81f2158 100644 --- a/docs/includes/reactivestreams/engine.adoc +++ b/docs/includes/reactivestreams/engine.adoc @@ -16,10 +16,23 @@ /////////////////////////////////////////////////////////////////////////////// += Helidon Reactive Engine + ifndef::rootdir[:rootdir: {docdir}/../..] :feature-name: Reactive Engine +== Contents + +- <> +- <> +- <> + +== Overview + +Helidon has its own set of reactive operators that have no dependencies outside of the Helidon ecosystem. +These operators can be used with `java.util.concurrent.Flow` based reactive streams. + include::{rootdir}/includes/dependencies.adoc[] [source,xml] @@ -30,10 +43,8 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Reactive Engine +== Usage -Helidon has its own set of reactive operators that have no dependencies outside of the Helidon ecosystem. -These operators can be used with `java.util.concurrent.Flow` based reactive streams. Stream processing operator chain can be easily constructed by `io.helidon.common.reactive.Multi`, or `io.helidon.common.reactive.Single` for streams with single value. @@ -134,22 +145,21 @@ In the situations when part of the operator chain needs to be prepared in advanc [source,java] .Combining operator chains: ---- - // Assembly of stream, nothing is streamed yet - Multi publisherStage = - Multi.just("foo", "bar") - .map(String::trim); +// Assembly of stream, nothing is streamed yet +Multi publisherStage = + Multi.just("foo", "bar") + .map(String::trim); - Function, Multi> processorStage = - upstream -> - upstream.map(String::toUpperCase); +Function, Multi> processorStage = + upstream -> + upstream.map(String::toUpperCase); - // Execution of pre-prepared stream - publisherStage - .compose(processorStage) - .map(s -> "Item received: " + s) - .forEach(System.out::println); +// Execution of pre-prepared stream +publisherStage + .compose(processorStage) + .map(s -> "Item received: " + s) + .forEach(System.out::println); > Item received: FOO > Item received: BAR ----- - +---- \ No newline at end of file diff --git a/docs/includes/reactivestreams/rsoperators.adoc b/docs/includes/reactivestreams/rsoperators.adoc index 2cebe44810c..1f416fd02b6 100644 --- a/docs/includes/reactivestreams/rsoperators.adoc +++ b/docs/includes/reactivestreams/rsoperators.adoc @@ -20,6 +20,18 @@ ifndef::rootdir[:rootdir: {docdir}/../..] :feature-name: Reactive Streams +== Contents + +- <> +- <> +- <> +- <> + +== Overview + +Helidon implements link:{microprofile-rs-operators-spec-url}[MicroProfile Reactive Streams Operators] specification which defines reactive operators and provides a standartized tool for manipulation with https://www.reactive-streams.org/[Reactive Streams]. Use it you want stick with MicroProfile ecosystem which guarantees source-level portability between different implementations of the specification. + + include::{rootdir}/includes/dependencies.adoc[] [source,xml] @@ -30,13 +42,9 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Reactive Streams Operators +== Usage -Implementation of -link:{microprofile-rs-operators-spec-url}[MicroProfile Reactive Streams Operators] -specification. A standardised tool for manipulation with https://www.reactive-streams.org/[Reactive Streams], -provides set of operators as so called stages, -and the builders to prepare graphs of stages for streams to be build from. +The specification provides a set of operators as so called stages, and the builders to prepare graphs of stages for streams to be build from. [source,java] .Example of simple closed graph usage: @@ -104,26 +112,31 @@ link:{microprofile-rs-operators-spec-url}#_graphs[Graphs] are pre-prepared strea [source,java] .Combining the graphs and running the stream: ---- - // Assembly of stream, nothing is streamed yet - PublisherBuilder publisherStage = - ReactiveStreams.of("foo", "bar") - .map(String::trim); +// Assembly of stream, nothing is streamed yet +PublisherBuilder publisherStage = + ReactiveStreams.of("foo", "bar") + .map(String::trim); - ProcessorBuilder processorStage = - ReactiveStreams.builder() - .map(String::toUpperCase); +ProcessorBuilder processorStage = + ReactiveStreams.builder() + .map(String::toUpperCase); - SubscriberBuilder subscriberStage = - ReactiveStreams.builder() - .map(s -> "Item received: " + s) - .forEach(System.out::println); +SubscriberBuilder subscriberStage = + ReactiveStreams.builder() + .map(s -> "Item received: " + s) + .forEach(System.out::println); - // Execution of pre-prepared stream - publisherStage - .via(processorStage) - .to(subscriberStage).run(); +// Execution of pre-prepared stream +publisherStage + .via(processorStage) + .to(subscriberStage).run(); > Item received: FOO > Item received: BAR ---- +== Reference + +* link:{microprofile-rs-operators-spec-url}[MicroProfile Reactive Streams Operators Specification] +* link:{microprofile-rs-operators-javadoc-url}[MicroProfile Reactive Streams Operators JavaDoc] +* link:https://github.com/eclipse/microprofile-reactive-streams-operators[MicroProfile Reactive Streams Operators on GitHub] diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index 37340fa0f01..e9784f6c8b8 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -174,7 +174,7 @@ Use prepared tools for repetitive use case scenarios. //Reactive Streams [CARD] .Reactive Streams -[icon=waves,link=../reactivestreams/overview.adoc] +[icon=waves,link=../reactivestreams/rsoperators.adoc] -- APIs to work with reactive streams in Helidon. -- diff --git a/docs/mp/reactivestreams/engine.adoc b/docs/mp/reactivestreams/engine.adoc index 6bf725c1cc3..10ca54bc811 100644 --- a/docs/mp/reactivestreams/engine.adoc +++ b/docs/mp/reactivestreams/engine.adoc @@ -24,6 +24,4 @@ include::{rootdir}/includes/mp.adoc[] -== Helidon Reactive Engine - include::{rootdir}/includes/reactivestreams/engine.adoc[lines=21..] diff --git a/docs/mp/reactivestreams/overview.adoc b/docs/mp/reactivestreams/overview.adoc deleted file mode 100644 index 47a380d7ab7..00000000000 --- a/docs/mp/reactivestreams/overview.adoc +++ /dev/null @@ -1,49 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Overview -:toc: -:toc-placement: preamble -:description: Reactive Streams support in Helidon -:keywords: helidon, mp, microprofile, reactivestreams -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -== Reactive Streams - -[PILLARS] -==== -[CARD] -.Helidon Reactive Engine -[icon=fa-cogs,link=engine.adoc] --- -A set of reactive operators. --- - -[CARD] -.MicroProfile Reactive Streams Operators -[icon=fa-book,link=rsoperators.adoc] --- -Microprofile implementation. --- -==== - -There are two handy apis for working with reactive streams available in Helidon, -one for working with `java.util.concurrent.Flow` -and another for `org.reactivestreams` based reactive components. \ No newline at end of file diff --git a/docs/mp/reactivestreams/rsoperators.adoc b/docs/mp/reactivestreams/rsoperators.adoc index aa34b071a7e..762776c7221 100644 --- a/docs/mp/reactivestreams/rsoperators.adoc +++ b/docs/mp/reactivestreams/rsoperators.adoc @@ -16,16 +16,13 @@ /////////////////////////////////////////////////////////////////////////////// -= Reactive Streams Operators -:toc: -:toc-placement: preamble -:description: MicroProfile Reactive Streams Operators support in Helidon MP -:keywords: helidon, mp, microprofile, reactivestreams -:feature-name: Reactive Streams += MicroProfile Reactive Streams Operators +:spec-name: MicroProfile Reactive Streams Operators +:description: {spec-name} support in Helidon MP +:keywords: helidon, mp, microprofile, reactive, operators +:microprofile-bundle: true :rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] -== Reactive Streams Operators - include::{rootdir}/includes/reactivestreams/rsoperators.adoc[lines=21..] diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index 2e38711d8c2..00c771677c5 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -113,7 +113,7 @@ Use prepared tools for repetitive use case scenarios. //Reactive Streams [CARD] .Reactive Streams -[icon=waves,link=reactivestreams/overview.adoc] +[icon=waves,link=reactivestreams/engine.adoc] -- APIs to work with reactive streams in Helidon. -- diff --git a/docs/se/reactivestreams/engine.adoc b/docs/se/reactivestreams/engine.adoc index 71b9d8d65cb..997d5e4b088 100644 --- a/docs/se/reactivestreams/engine.adoc +++ b/docs/se/reactivestreams/engine.adoc @@ -22,5 +22,6 @@ :feature-name: Reactive Engine :rootdir: {docdir}/../.. -include::{rootdir}/includes/common-attributes.ado -include::{rootdir}/includes/reactivestreams/engine.adoc[] +include::{rootdir}/includes/se.adoc[] + +include::{rootdir}/includes/reactivestreams/engine.adoc[lines=21..] diff --git a/docs/se/reactivestreams/overview.adoc b/docs/se/reactivestreams/overview.adoc deleted file mode 100644 index d29c730f0b6..00000000000 --- a/docs/se/reactivestreams/overview.adoc +++ /dev/null @@ -1,49 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Overview -:toc: -:toc-placement: preamble -:description: Reactive Streams support in Helidon -:keywords: helidon, se, microprofile, reactivestreams -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== Reactive Streams - -[PILLARS] -==== -[CARD] -.Helidon Reactive Engine -[icon=fa-cogs,link=engine.adoc] --- -A set of reactive operators. --- - -[CARD] -.MicroProfile Reactive Streams Operators -[icon=fa-book,link=rsoperators.adoc] --- -Microprofile implementation. --- -==== - -There are two handy apis for working with reactive streams available in Helidon, -one for working with `java.util.concurrent.Flow` -and second for `org.reactivestreams` based reactive components. \ No newline at end of file diff --git a/docs/se/reactivestreams/rsoperators.adoc b/docs/se/reactivestreams/rsoperators.adoc index bbc910a3c12..e548ea87bae 100644 --- a/docs/se/reactivestreams/rsoperators.adoc +++ b/docs/se/reactivestreams/rsoperators.adoc @@ -16,9 +16,7 @@ /////////////////////////////////////////////////////////////////////////////// -= Reactive Streams Operators -:toc: -:toc-placement: preamble += MicroProfile Reactive Streams Operators :description: MicroProfile Reactive Streams Operators support in Helidon SE :keywords: helidon, se, microprofile, reactivestreams :feature-name: Reactive Streams diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 3a046131c49..f9d2bbbf5a3 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -199,7 +199,6 @@ backend: type: "icon" value: "waves" sources: - - "overview.adoc" - "engine.adoc" - "rsoperators.adoc" - type: "MENU" @@ -447,7 +446,6 @@ backend: type: "icon" value: "waves" sources: - - "overview.adoc" - "engine.adoc" - "rsoperators.adoc" - type: "MENU" From 0651f1305b04365e5493f9502a61aabb0233e324 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 15 Jul 2022 16:09:19 -0500 Subject: [PATCH 29/51] SE metrics doc reorg (#4543) --- docs/includes/attributes.adoc | 2 + .../metrics/metrics-capable-components.adoc | 4 +- docs/includes/metrics/metrics-config.adoc | 21 +-- docs/includes/metrics/metrics-shared.adoc | 171 +++++++++++++++++ docs/includes/metrics/micrometer-shared.adoc | 15 +- .../metrics/prometheus-exemplar-support.adoc | 12 +- docs/mp/metrics/metrics.adoc | 77 +------- docs/mp/metrics/micrometer.adoc | 4 +- .../metrics/metrics-capable-components.adoc | 4 +- docs/se/metrics/metrics.adoc | 176 +++++++++++++----- docs/se/metrics/micrometer.adoc | 49 ++++- docs/se/metrics/prometheus.adoc | 60 ------ docs/sitegen.yaml | 3 +- 13 files changed, 387 insertions(+), 211 deletions(-) create mode 100644 docs/includes/metrics/metrics-shared.adoc delete mode 100644 docs/se/metrics/prometheus.adoc diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 77b2c6cdfc5..8ba67dcd315 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -98,6 +98,7 @@ endif::[] :microprofile-metrics-spec-url: {microprofile-metrics-base-url}/microprofile-metrics-spec-{version-lib-microprofile-metrics-api}.html :microprofile-metrics-javadoc-url: {microprofile-metrics-base-url}/apidocs :microprofile-metrics-javadoc-annotation-url: {microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/annotation +:microprofile-metrics-javadoc-metric-url: {microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics :microprofile-tracing-base-url: {microprofile-base-url}/microprofile-tracing-{version-lib-microprofile-tracing} :microprofile-tracing-spec-url: {microprofile-tracing-base-url}/microprofile-tracing-spec-{version-lib-microprofile-tracing}.html @@ -187,6 +188,7 @@ endif::[] :metrics-mp-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.metrics :metrics-serviceapi-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.serviceapi :micrometer-javadoc-base-url: {javadoc-base-url}/io.helidon.integrations.micrometer +:prometheus-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.prometheus :mp-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.cors :mp-tyrus-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.tyrus :mp-restclient-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.restclient diff --git a/docs/includes/metrics/metrics-capable-components.adoc b/docs/includes/metrics/metrics-capable-components.adoc index f7acb77356b..e84f2332665 100644 --- a/docs/includes/metrics/metrics-capable-components.adoc +++ b/docs/includes/metrics/metrics-capable-components.adoc @@ -38,7 +38,7 @@ This document explains Helidon {h1-prefix} metrics-capable components and applic // end::preamble[] // tag::all-beginning-text[] -== Introduction + Think of Helidon metrics in three related but different parts: * The Helidon metrics API. @@ -170,7 +170,7 @@ Doing so allows your code to operate regardless of whether the full-featured met ---- This module defines the metrics API: `RegistryFactory`, `MetricRegistry`, and the various metrics themselves. -ifeval::["{h1-prefix}" == "SE"] +ifdef::se-flavor[] . To permit the use of the built-in metrics web service support for the `/metrics` endpoint, add this dependency: + [source,xml] diff --git a/docs/includes/metrics/metrics-config.adoc b/docs/includes/metrics/metrics-config.adoc index ec900a18ce3..df7f0f2bd2a 100644 --- a/docs/includes/metrics/metrics-config.adoc +++ b/docs/includes/metrics/metrics-config.adoc @@ -43,9 +43,11 @@ The rest of this section illustrates some of the most common scenarios: * <> * <> * <> +ifdef::mp-flavor[] * <> -[[config-disable]] +endif::[] +[#config-disable] ==== Disable Metrics Subsystem .Disabling metrics entirely @@ -64,7 +66,7 @@ metrics: endif::[] Helidon does not update metrics, and the `/metrics` endpoints respond with `404` plus a message that the metrics subsystem is disabled. -[[config-selective]] +[#config-selective] ==== Disable Selected Metrics You can be even more selective. Within a registry type you can configure up to two regular expression patterns: @@ -110,7 +112,7 @@ endif::[] This setting excludes metrics with names starting with `myapp.supplier` _except_ for the metric `myapp.supplier.updates`. The `exclude` and `include` values are regular expressions. -[[config-kpi]] +[#config-kpi] ==== Collecting Basic and Extended Key Performance Indicator (KPI) Metrics Any time you include the Helidon metrics module in your application, Helidon tracks two basic performance indicator metrics: @@ -146,23 +148,16 @@ metrics: ---- endif::[] -[[config-rest-request]] +[#config-rest-request] +ifdef::mp-flavor[] ==== Enable `REST.request` metrics .Controlling REST request metrics -ifdef::mp-flavor[] [source,properties] ---- metrics.rest-request-enabled=true ---- -endif::[] -ifdef::se-flavor[] -[source,yaml] ----- -metrics: - rest-request-enabled: true ----- -endif::[] Helidon will automatically register and update `SimpleTimer` metrics for every REST endpoint in your service. +endif::[] // end::config-examples[] \ No newline at end of file diff --git a/docs/includes/metrics/metrics-shared.adoc b/docs/includes/metrics/metrics-shared.adoc new file mode 100644 index 00000000000..aeb5551c4ed --- /dev/null +++ b/docs/includes/metrics/metrics-shared.adoc @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 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. + +/////////////////////////////////////////////////////////////////////////////// + +// tag::overview[] + +ifndef::rootdir[:rootdir: {docdir}/../..] +ifndef::flavor-lc[:flavor-lc: se] +:description: Helidon metrics +:keywords: helidon, metrics +:writing-code-content: code which explicitly invokes the metrics API to register metrics, retrieve previously-registered metrics, and update metric values. + +* a unified way for +ifdef::mp-flavor[MicroProfile] +ifdef::se-flavor[Helidon] +servers to export monitoring data--telemetry--to management agents, and +* a unified Java API which all application programmers can use to register and update metrics to expose telemetry data from their services. +ifdef::mp-flavor[] +* support for metrics-related annotations. + +endif::[] +Learn more about the https://github.com/eclipse/microprofile-metrics/releases/tag/{version-lib-microprofile-metrics-api}[MicroProfile Metrics specification]. + +// end::overview[] + +// tag::usage-body[] +=== Instrumenting Your Service + +You add metrics to your service +ifdef::se-flavor[] +by writing {writing-code-content} +endif::[] +ifdef::mp-flavor[] +in these ways: + +* Annotate bean methods--typically your REST resource endpoint methods (the Java code that receives incoming REST requests); Helidon automatically registers these metrics and updates them when the annotated methods are invoked via CDI. +* Write {writing-code-content} +* Configure some simple `REST.request` metrics which Helidon automatically registers and updates for all REST resource endpoints. +endif::[] + +Later sections of this document describe how to do +ifdef::mp-flavor[each of these.] +ifdef::se-flavor[this.] + +=== Categorizing Types of Metrics +Helidon distinguishes among three general _types_, or scopes, of metrics, as described in the MP metrics specification. + +.Types (scopes) of metrics +[%autowidth] +|==== +| Type/scope | Typical Usage + +| base | Mandated by the MP metrics specification, such as OS or Java runtime measurements (available heap, disk space, etc.). +| vendor | Implemented by vendors, including the `REST.request` metrics and other key performance indicator measurements (described in later sections). +| application | Declared via annotations or programmatically registered by your service code. +|==== + +When you add metrics annotations to your service code, Helidon registers the resulting metrics as type `application`. + +=== Metric Registries +A _metric registry_ collects registered metrics of a given type. Helidon supports three registries, one for each of the three metrics types. + +When you add code to your service to create a metric programmatically, the code first locates the appropriate registry and then registers the metric with that registry. + +=== Retrieving Metrics Reports from your Service +When you add the metrics dependency to your project, Helidon automatically provides a built-in REST endpoint `/metrics` which responds with a report of the registered metrics and their values. + +Clients can request a particular output format. + +.Formats for `/metrics` output +[%autowidth] +|==== +| Format | Requested by + +| OpenMetrics (Prometheus) | default (`text/plain`) +| JSON | Header `Accept: application/json` +|==== + +Clients can also limit the report by appending the metric type to the path: + +* `/metrics/base` +* `/metrics/vendor` +* `/metrics/application` + +Further, clients can narrow down to a specific metric name by adding the name as a subpath such as `/metrics/application/myCount`. + +[source,bash] +.Example Reporting: Prometheus format +---- +curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/metrics/ +---- + +[listing] +---- +# TYPE base:classloader_total_loaded_class_count counter +# HELP base:classloader_total_loaded_class_count Displays the total number of classes that have been loaded since the Java virtual machine has started execution. +base:classloader_total_loaded_class_count 3157 +---- + + +.Example Reporting: JSON format +[source,bash] +---- +curl -s -H 'Accept: application/json' -X GET http://localhost:8080/metrics/ +---- + +[listing] +---- +{ + "base" : { + "memory.maxHeap" : 3817865216, + "memory.committedHeap" : 335544320, + } +} +---- + +In addition to your application metrics the reports contain other +metrics of interest such as system and VM information. + +// end::usage-body[] + +// tag::metric-registry-api[] +=== The `MetricRegistry` API +To register or look up metrics programmatically, your service code uses one of the three link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/MetricRegistry.html[`MetricRegistry`] instances (base, vendor, and application) which Helidon furnishes automatically. + +ifdef::mp-flavor[] +To get a `MetricRegistry` reference + +* `@Inject` the metric registry you want, perhaps also using the link:{microprofile-metrics-javadoc-annotation-url}/RegistryType.html[`@RegistryType`] annotation to select the registry type, or +* Get a Helidon link:{metrics-javadoc-base-url}/RegistryFactory.html[`RegistryFactory`]; either ++ +-- +** `@Inject` `RegistryFactory` or +** Invoke one of the static `getInstance` methods on `RegistryFactory` +-- ++ +Then invoke `getRegistry` on the `RegistryFactory` instance. +endif::[] +ifdef::se-flavor[] +To get a `MetricRegistry` reference, first get a Helidon link:{metrics-javadoc-base-url}/RegistryFactory.html[`RegistryFactory`]. +Then invoke `getRegistry` on the `RegistryFactory` instance. +endif::[] + +The `MetricRegistry` allows your code to register new metrics, look up previously-registered metrics, and remove metrics. +// end::metric-registry-api[] + +// tag::example-apps[] +Helidon {flavor-uc} includes several prewritten example applications illustrating aspects of metrics: + +* link:{helidon-github-tree-url}/examples/metrics/filtering/{flavor-lc}[Enabling/disabling metrics] using +ifdef::se-flavor[`MetricsSettings`] +ifdef::mp-flavor[configuration] +ifdef::se-flavor[] +* link:{helidon-github-tree-url}/examples/metrics/kpi[Controlling key performance indicator metrics] using configuration and `KeyPerformanceIndicatorMetricsSettings`. +endif::[] + +// end::example-apps[] \ No newline at end of file diff --git a/docs/includes/metrics/micrometer-shared.adoc b/docs/includes/metrics/micrometer-shared.adoc index 6dc5cdf488d..97be0d2a64b 100644 --- a/docs/includes/metrics/micrometer-shared.adoc +++ b/docs/includes/metrics/micrometer-shared.adoc @@ -135,12 +135,15 @@ meter registries. // tag::accessing-endpoint-intro[] === Accessing the Helidon Micrometer Endpoint -Helidon automatically creates a REST endpoint which clients can access to retrieve Micrometer metrics, by default at the `/micrometer` endpoint. + +ifdef::mp-flavor[Helidon MP Micrometer integration automatically creates] +ifdef::se-flavor[Your application can easily have Helidon create] +a REST endpoint which clients can access to retrieve Micrometer metrics, by default at the `/micrometer` endpoint. ifdef::se-flavor[] -Within Helidon, each type of meter registry is paired with code that examines the incoming HTTP request and decides +Within Helidon, each type of meter registry is paired with some code that examines the incoming HTTP request to `/micrometer` and decides whether the request matches up with the associated meter registry. The first pairing that accepts the request -returns the response. +returns the response. You will need to take advantage of this if your application uses additional meter registries beyond what Helidon automatically provides _and_ you want those meter registries reflected in the output from the `/micrometer` REST endpoint. endif::[] // end::accessing-endpoint-intro[] @@ -148,7 +151,7 @@ endif::[] == Configuration You can configure the Helidon Micrometer REST service as you can other built-in Helidon services by adding configuration settings under the `micrometer` top-level key. -include::{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[leveloffset=+1] +include::{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[tag=config,leveloffset=+1] By default, Helidon Micrometer integration exposes the `/micrometer` endpoint. You can override the path using @@ -183,5 +186,9 @@ endif::[] can create, look up, and update metrics programmatically using the Micrometer `MeterRegistry` API. The link:{micrometer-api-url}[Micrometer concepts document] provides a good starting point for learning how to use Micrometer's interfaces and classes. // end::use-micrometer-api[] +// tag::example-apps[] +Helidon {flavor-uc} includes an link:{helidon-github-tree-url}/examples/integrations/micrometer/{flavor-lc}[example application] which uses Micrometer support. +// end::example-apps[] + // end::all-micrometer[] diff --git a/docs/includes/metrics/prometheus-exemplar-support.adoc b/docs/includes/metrics/prometheus-exemplar-support.adoc index 06a24481202..8b2c3cb2c87 100644 --- a/docs/includes/metrics/prometheus-exemplar-support.adoc +++ b/docs/includes/metrics/prometheus-exemplar-support.adoc @@ -47,7 +47,7 @@ link:https://www.merriam-webster.com/dictionary/exemplar[_exemplar_] - one that -- Merriam-Webster Dictionary -- -In the context of metrics, an _exemplar_ for a given metric is a specific trace which, in some sense, made a typical contribution to the metric's value. For example, an exemplar for a `SimpleTimer` might be a trace in which the duration it contributed to the value of a `SimpleTimer` is near the mean of the durations over all traces. +In the context of metrics, an _exemplar_ for a given metric is a specific sample which, in some sense, made a typical contribution to the metric's value. For example, an exemplar for a `SimpleTimer` might be a sample in which the duration it contributed to the value of a `SimpleTimer` is near the mean of the durations over all samples. The metrics output identifies the exemplar sample using the trace ID of the trace which triggered that sample. include::{rootdir}/includes/dependencies.adoc[] @@ -91,13 +91,13 @@ Helidon chooses the representative examplar for each value using information tha .Selection of exemplars for types of metrics [%autowidth] |==== -|Metric Type| Example | Sample Selected as Exemplar +|Metric Value Type| Example | Sample Selected as Exemplar | corresponds directly to a specific sample | minimum or maximum of a value | any sample with that exact value -| collecs samples into bins (quantiles) +| collects samples into bins (quantiles) | histogram (as with timers) | any sample from the bin @@ -126,6 +126,12 @@ But some consumers, such as trace collectors and their U/Is, understand the exem == Examples +ifdef::se-flavor[] +Helidon includes an link:{helidon-github-tree-url}/examples/metrics/exemplar[example application], based on the QuickStart application, which illustrates exemplar support. +endif::[] + +Once you enable exemplar support you can see the exemplars in the metrics output. + .Exemplar output - `Timer` [listing,subs="quotes"] ---- diff --git a/docs/mp/metrics/metrics.adoc b/docs/mp/metrics/metrics.adoc index 30a7b317c3a..c88aa00edb2 100644 --- a/docs/mp/metrics/metrics.adoc +++ b/docs/mp/metrics/metrics.adoc @@ -40,10 +40,7 @@ include::{rootdir}/includes/mp.adoc[] == Overview Helidon MP metrics implements the MicroProfile Metrics specification, providing: -* a unified way for MicroProfile servers to export monitoring data--telemetry--to management agents, and -* a unified Java API which all application programmers can use to expose telemetry data from their services. - -Learn more about the https://github.com/eclipse/microprofile-metrics/releases/tag/{version-lib-microprofile-metrics-api}[MicroProfile Metrics specification]. +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=overview] include::{rootdir}/includes/dependencies.adoc[] @@ -57,56 +54,8 @@ include::{rootdir}/includes/dependencies.adoc[] == Usage -=== Instrumenting your Service -You can add metrics to your service in these ways: - -* Annotate bean methods--typically your REST resource endpoint methods (the Java code that receives incoming REST requests); Helidon automatically registers these metrics and updates them when the annotated methods are invoked via CDI. -* Write code in your service which explicitly invokes the metrics API to register metrics, retrieve previously-registered metrics, and update metric values. -* Configure some simple `REST.request` metrics which Helidon automatically registers and updates for all REST resource endpoints. - -Later sections of this document describe how to do each of these. - -=== Categorizing Types of Metrics -Helidon distinguishes among three general _types_, or scopes, of metrics, as described in the MP metrics specification. - -.Types (scopes) of metrics -[%autowidth] -|==== -| Type/scope | Typical Usage - -| base | Mandated by the MP metrics specification, such as OS or Java runtime measurements (available heap, disk space, etc.). -| vendor | Implemented by vendors, including the `REST.request` metrics and other key performance indicator measurements (described in later sections). -| application | Declared via annotations or programmatically registered by your service code. -|==== - -When you add metrics annotations to your service code, Helidon registers the resulting metrics as type `application`. - -=== Metric Registries -A _metric registry_ collects registered metrics of a given type. Helidon supports three registries, one for each of the three metrics types. - -When you add code to your service to create a metric programmatically, the code first locates the appropriate registry and then the registers the metric with that registry. - -=== Retrieving Metrics Reports from your Service -When you add the metrics dependency to your project, Helidon automatically provides a built-in REST endpoint `/metrics` which responds with a report of the registered metrics and their values. +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-body] -Clients can request a particular output format. - -.Formats for `/metrics` output -[%autowidth] -|==== -| Format | Requested by - -| OpenMetrics (Prometheus) | default -| JSON | Header `Accept: application/json` -|==== - -Clients can also limit the report by appending the metric type to the path: - -* `/metrics/base` -* `/metrics/vendor` -* `/metrics/application` - -Further, clients can narrow down to a specific metric name by adding the name as a subpath such as `/metrics/application/myCount`. == API @@ -154,28 +103,18 @@ You can also add `@Metric` on a constructor or method parameter to trigger injec Helidon automatically looks up the metric referenced from any injection site and provides a reference to the metric. Your code then simply invokes methods on the injected metric. -=== The `MetricRegistry` API -To register or look up metrics programmatically, your service code uses one of the three link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/MetricRegistry.html[`MetricRegistry`] instances (base, vendor, and application) which Helidon furnishes automatically. - -To get a `MetricRegistry` reference - -* `@Inject` the metric registry you want, perhaps also using the link:{microprofile-metrics-javadoc-annotation-url}/RegistryType.html[`@RegistryType`] annotation to select the registry type, or -* Get a Helidon link:{metrics-javadoc-base-url}/RegistryFactory.html[`RegistryFactory`]; either -+ --- -** `@Inject` `RegistryFactory` or -** Invoke one of the static `getInstance` methods on `RegistryFactory` --- -+ -Then invoke `getRegistry` on the `RegistryFactory` instance. - -The `MetricRegistry` allows your code to register new metrics, look-up previously-registered metrics, and remove metrics. +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=metric-registry-api] // Here's Configuration. include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro] == Examples +Helidon MP includes a prewritten example application illustrating +link:{helidon-github-tree-url}/examples/metrics/filtering/mp[enabling/disabling metrics] using configuration. + +The rest of this section contains other examples of working with metrics: + * <> * <> diff --git a/docs/mp/metrics/micrometer.adoc b/docs/mp/metrics/micrometer.adoc index a32f2aa87df..41d3ebff084 100644 --- a/docs/mp/metrics/micrometer.adoc +++ b/docs/mp/metrics/micrometer.adoc @@ -65,9 +65,9 @@ include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=use-micrometer-ap include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=configuring-endpoint] == Examples -Helidon provides an link:{helidon-github-tree-url}/examples/integrations/micrometer/mp[example MP application] using Micrometer integration. +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=example-apps] -The examples below enhance the Helidon MP QuickStart application to track (by time and invocation count) all `GET` methods and to count all requests for a personalized greeting. +The examples below take you step-by-step through the process of enhancing the Helidon MP QuickStart application to track (by time and invocation count) all `GET` methods and to count all requests for a personalized greeting. === Add Micrometer annotations diff --git a/docs/se/metrics/metrics-capable-components.adoc b/docs/se/metrics/metrics-capable-components.adoc index 4a13b9c882f..956bae4910e 100644 --- a/docs/se/metrics/metrics-capable-components.adoc +++ b/docs/se/metrics/metrics-capable-components.adoc @@ -26,9 +26,7 @@ include::{rootdir}/includes/se.adoc[] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=preamble] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=all-beginning-text] -include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=writing-code-beginning] +include::{rootdir}/includes/metrics/metrics-capable-components.adoc[tags=preamble;all-beginning-text;writing-code-intro] [[writing_SE,SE Application Techniques]] ==== Writing and Packaging a Metrics-capable _Helidon SE Application_ diff --git a/docs/se/metrics/metrics.adoc b/docs/se/metrics/metrics.adoc index b043a3b1f7e..77edc04a909 100644 --- a/docs/se/metrics/metrics.adoc +++ b/docs/se/metrics/metrics.adoc @@ -16,20 +16,29 @@ /////////////////////////////////////////////////////////////////////////////// -= Metrics += Metrics in Helidon SE :description: Helidon metrics :keywords: helidon, metrics -:feature-name: Metrics +:feature-name: metrics :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -Helidon SE provides the following to support metrics: +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview +Helidon SE metrics is inspired by--but does not fully implement--the MicroProfile Metrics specification. +In particular, Helidon metrics furnishes +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=overview] -1. The endpoint `/metrics`: A configurable endpoint that exposes metrics information in JSON format (as specified by the - MicroProfile Metrics specification) or in plain text (for Prometheus metrics). -2. A base set of metrics, available at `/metrics/base`, as specified by the MicroProfile Metrics specification. -3. A set of Helidon-specific metrics, available at `/metrics/vendor` include::{rootdir}/includes/dependencies.adoc[] @@ -42,8 +51,15 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Using Metrics in Your Application -To enable Metrics, register it with the WebServer. +== Usage + +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-body] + +=== Enabling the `MetricsSupport` REST Service +To enable the metrics REST endpoint: + +. Create an instance of link:{metrics-serviceapi-javadoc-base-url}/MetricsSupport.html[`MetricsSupport`], either directly as shown below or using its link:{metrics-serviceapi-javadoc-base-url}/MetricsSupport.Builder.html[`Builder`]. +. Include the `MetricsSupport` instance in your application's routing. [source,java] ---- @@ -55,20 +71,79 @@ Routing.builder() .build(); ---- -Then you can use metrics in your service. +== API +To work with Helidon Metrics in your code, follow these steps: + +. Use a static method on the link:{metrics-javadoc-base-url}/RegistryFactory.html[`RegistryFactory`] to get a reference to the link:{microprofile-metrics-javadoc-url}/org/eclipse/microprofile/metrics/MetricRegistry.html[`MetricRegistry`] instance you want to use. +. Use the `MetricRegistry` instance to register new metrics and look up previously-registered metrics. +. Use the metric reference returned from the `MetricRegistry` to update the metric or get its value. + +You can also use the `MetricRegistry` to remove an existing metric. + +=== Helidon Metrics API + +The Helidon Metrics API is aligned with the link:{microprofile-metrics-javadoc-url}[MicroProfile Metrics API]. +That API defines the classes and interfaces for metric types, metric registries, and other related items. Helidon Metrics reuses that API for classes and interfaces (but not for annotations which Helidon SE does not support). + +The following table summarizes the metric types. + +.Metric Types +[%autowidth] +|==== +| Metric Type | Usage + +| link:{microprofile-metrics-javadoc-metric-url}/Counter.html[`Counter`] +| Monotonically increasing count of events. + +| link:{microprofile-metrics-javadoc-metric-url}ConcurrentGauge.html[`ConcurrentGauge`] +| Increasing and decreasing measurement of currently-executing blocks of code. + +| link:{microprofile-metrics-javadoc-metric-url}/Gauge.html[`Gauge`] +| Access to a value managed by other code in the service. + +| link:{microprofile-metrics-javadoc-metric-url}/Meter.html[`Meter`] +| Count of invocations and how frequently invocations have occurred. + +| link:{microprofile-metrics-javadoc-metric-url}/SimpleTimer.html[`SimpleTimer`] +| Count of invocations and the total duration consumed by those invocations. + +| link:{microprofile-metrics-javadoc-metric-url}/Timer.html[`Timer`] +| Frequency of invocations and the distribution of how long the invocations take. + +|==== + +Each metric type has its own set of methods for updating and retrieving the metric's value. + + +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=metric-registry-api] + +// Here's Configuration. +include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-intro] + +== Examples + +include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=example-apps] + +The rest of this section shows how to add a metric to your code and how to configure the Helidon metrics subsystem. + +=== Example Application Code + +The following example illustrates registering and updating a new `Counter` in application code. + +.Define and use a `Counter` [source,java] -.Define and use a Metrics Counter ---- import io.helidon.metrics.api.RegistryFactory; import org.eclipse.microprofile.metrics.Counter; import org.eclipse.microprofile.metrics.MetricRegistry; +//... public class MyService implements Service { private final MetricRegistry registry = RegistryFactory.getInstance() - .getRegistry(MetricRegistry.Type.APPLICATION); <1> - private final Counter accessCtr = registry.counter("accessctr"); <2> + .getRegistry(MetricRegistry.Type.APPLICATION); // <1> + private final Counter accessCtr = registry.counter("accessctr"); // <2> @Override public void update(Routing.Rules rules) { @@ -78,54 +153,63 @@ public class MyService implements Service { } private void countAccess(ServerRequest request, ServerResponse response) { - accessCtr.inc(); //<3> + accessCtr.inc(); // <3> request.next(); } } ---- +<1> Get the application metric registry. +<2> Create a counter in that registry. +<3> Increment the counter for every request. -<1> Get the application metrics registry -<2> Create a counter in that registry -<3> Increment the counter for every request - -NOTE: Helidon-provided endpoints for `/metrics` do their work synchronously, using the same thread on which the request arrived via Netty. To prevent performance degradation, avoid including long-running code that can be invoked by these handlers while Helidon is responding to the metric. + +NOTE: Helidon-provided endpoints for `/metrics` do their work synchronously, using the same thread on which the request arrived via Netty. To prevent performance degradation, avoid including long-running code that can be invoked by these handlers while Helidon is responding to the metric. For example, if you implement your own application-specific metric types, you will write logic to format the JSON and OpenMetrics output for those metric types. Helidon invokes this formatting logic whenever a client accesses the /metrics endpoints, so make that formatting code as efficient as possible. +// example configuration +include::{rootdir}/includes/metrics/metrics-config.adoc[tag=config-examples] + +== Additional Information +=== Support for the Prometheus Metrics API +Helidon provides optional support for the Prometheus metrics API. -== Accessing Metrics Endpoint +To use it, your service registers Prometheus support with your routing set-up. +You can customize its configuration. For information about using Prometheus, see the +Prometheus documentation: https://prometheus.io/docs/introduction/overview/. -Access metrics data via the `/metrics` endpoint. Two reporting formats -are supported. The HTTP Accept header sent by the client determines -the reporting format: +NOTE: Helidon's fully-functional, built-in metrics implementation supports Prometheus (OpenMetrics) output. Use this optional support only if you want to use the Prometheus API from your application code. -1. JSON format - used when the HTTP Accept header matches `application/json` -2. Prometheus text format - used when the HTTP Accept header is `text/plain` - or otherwise does not match `application/json` +[#prom-maven-coordinates] +==== Maven Coordinates -[source,bash] -.Example Reporting: Prometheus format +.Dependency for Helidon Prometheus API support +[source,xml] ---- -curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/metrics/ -# TYPE base:classloader_total_loaded_class_count counter -# HELP base:classloader_total_loaded_class_count Displays the total number of classes that have been loaded since the Java virtual machine has started execution. -base:classloader_total_loaded_class_count 3157 + + io.helidon.metrics + helidon-metrics-prometheus + ---- -[source,bash] -.Example Reporting: JSON format ----- -curl -s -H 'Accept: application/json' -X GET http://localhost:8080/metrics/ | json_pp -{ - "base" : { - "memory.maxHeap" : 3817865216, - "memory.committedHeap" : 335544320, - } -} +[#prom-usage] +==== Usage +Your application code uses the Prometheus API to manage metrics. +To expose those metrics to clients via a REST endpoint, your code uses the `PrometheusSupport` interface which Helidon provides. + +[#prom-api] +==== API +Your code creates a link:{prometheus-javadoc-base-url}/PrometheusSupport.html[`PrometheusSupport`] object either using a static factory method (shown in the following example) or by using its link:{prometheus-javadoc-base-url}/PrometheusSupport.Builder.html[`Builder`]. + +[source,java] ---- +import io.helidon.metrics.prometheus.PrometheusSupport; -In addition to your application metrics the reports contain other -metrics of interest such as system and VM information. +Routing.builder() + .register(PrometheusSupport.create()) + .register("/myapp", new MyService()) + .build(); +---- -For full details see the link:{microprofile-metrics-spec-url}[MicroProfile Metrics] specification. -The Metrics component in Helidon SE is the core for the Helidon MP implementation of the MicroProfile Metrics specification. +This example uses the default Prometheus `CollectorRegistry`. By default, the `PrometheusSupport` and exposes its REST endpoint at the path +`/metrics`. Use the builder obtained by `PrometheusSupport.builder()` to +configure a different `CollectorRegistry` or a different path. \ No newline at end of file diff --git a/docs/se/metrics/micrometer.adoc b/docs/se/metrics/micrometer.adoc index 978c87a281d..bd224aa3236 100644 --- a/docs/se/metrics/micrometer.adoc +++ b/docs/se/metrics/micrometer.adoc @@ -23,14 +23,45 @@ include::{rootdir}/includes/se.adoc[] +== Contents +- <> +- <> +- <> +- <> +- <> +- <> +- <> + include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=intro] include::{rootdir}/includes/metrics/micrometer-shared.adoc[tags=prereq] -== Using Micrometer in Your Application -You need to make two types of changes to your application to use Helidon SE integration with Micrometer: +== Usage + +=== Registering and Updating Meters +Your code +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=use-micrometer-api] + +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=accessing-endpoint-intro] + +== API + +=== The Helidon Micrometer API + +Helidon provides no special API for dealing with Micrometer meters and meter registries beyond what Micrometer offers itself. + +Helidon _does_ give you an easy way to expose a REST endpoint to report the meters stored in the Micrometer meter registry. The link:{micrometer-javadoc-base-url}/io/helidon/integrations/micrometer/MicrometerSupport.html[`MicrometerSupport`] interface exposes static methods to directly create an instance of `MicrometerSupport` and to return a link:{micrometer-javadoc-base-url}/io/helidon/integrations/micrometer/MicrometerSupport.Builder.html[`Builder`] instance so your code can fine-tune how the REST service behaves. + +// Configuration +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=configuring-endpoint] + +== Examples + +include::{rootdir}/includes/metrics/micrometer-shared.adoc[tag=example-apps] + +The rest of this section takes you through the process of changing your application to use Helidon SE integration with Micrometer: . Register an instance of link:{micrometer-javadoc-base-url}/io/helidon/integrations/micrometer/MicrometerSupport.html[`MicrometerSupport`] with the web server. -. Create meters using the meter registry which `MicrometerSupport` manages and update those meters. +. Create meters using the meter registry managed by Helidon's `MicrometerSupport` and then update and query those meters. === Register an Instance of `MicrometerSupport` with the Web Server @@ -47,7 +78,7 @@ Routing.builder() .build(); ---- <1> Create the `MicrometerSupport` instance, using the default built-in Prometheus meter registry. -<2> Register the support instance as a service; by default, `MicrometerSupport` exposes the endpoint as `/micrometer`. +<2> Register the `MicrometerSupport` instance as a service; by default, `MicrometerSupport` exposes the endpoint as `/micrometer`. <3> Pass the `MicrometerSupport` object's meter registry to your service for use in creating and updating meters. === Create and Update Meters in your Application Service @@ -80,7 +111,7 @@ public class MyService implements Service { ---- <1> Use the Micrometer meter registry to create the request counter. <2> Add routing for any request to invoke the method which counts requests by updating the counter. -<3> Update the counter and delegate the rest of the request processing to the next handler in the chain. +<3> Update the counter and then delegate the rest of the request processing to the next handler in the chain. The example above enrolls the built-in Prometheus meter registry with the default Prometheus registry configuration. You can change the default setup for built-in registries, and you can enroll other meter registries your application @@ -133,8 +164,8 @@ its `accept` method sets the payload in the HTTP response using the registry's m . Write a `Function` which accepts a `ServerRequest` and returns an `Optional` + + -In general, your function looks at the request--the `Content-Type`, query parameters, etc.--to -decide whether your handler should respond to the request. +Typically, the function examines the request--the `Content-Type`, query parameters, etc.--to +decide whether the corresponding handler should respond to the request. If so, your function should instantiate your `Handler` and return an `Optional.of(theHandlerInstance)`; otherwise, your function should return `Optional.empty()`. + + @@ -174,3 +205,7 @@ Note that the `Handler` which your function returns typically has a reference to in preparing the response. +== Additional Information + +The link:https://micrometer.io[Micrometer website] describes the project as a whole and has links to more information. + diff --git a/docs/se/metrics/prometheus.adoc b/docs/se/metrics/prometheus.adoc deleted file mode 100644 index 48b70a955a0..00000000000 --- a/docs/se/metrics/prometheus.adoc +++ /dev/null @@ -1,60 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Prometheus Metrics -:description: Helidon Prometheus metrics -:keywords: helidon, metrics, prometheus -:feature-name: Prometheus Metrics -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Helidon WebServer can serve Prometheus metrics. - -This document describes how to register Prometheus support with WebServer and how to customize -the configuration. For information about using Prometheus, see the -Prometheus documentation: https://prometheus.io/docs/introduction/overview/. - -Note that Helidon has an in-built metrics implementation. See xref:../metrics/metrics.adoc[Helidon Metrics]. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.metrics - helidon-metrics-prometheus - ----- - -== Using Prometheus metrics in your application -To enable Prometheus metrics, register it with the web server. - -[source,java] ----- -import io.helidon.metrics.prometheus.PrometheusSupport; - -Routing.builder() - .register(PrometheusSupport.create()) - .register("/myapp", new MyService()) - .build(); ----- - -This example uses the default `CollectorRegistry` and exposes an endpoint -`/metrics`. You can use fluent API builder obtained by `PrometheusSupport.builder()` to -configure a different `CollectorRegistry` or a different path. diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index f9d2bbbf5a3..6e864efec6d 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -171,10 +171,9 @@ backend: value: "av_timer" sources: - "metrics.adoc" - - "metrics-capable-components.adoc" - "micrometer.adoc" - - "prometheus.adoc" - "prometheus-exemplar-support.adoc" + - "metrics-capable-components.adoc" - type: "PAGE" title: "OpenAPI" source: "openapi.adoc" From ae91cdb6142f4b133181aaa7edd7165577735161 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 15 Jul 2022 16:10:43 -0500 Subject: [PATCH 30/51] OpenAPI doc update: mostly SE but some refactoring of MP as well (#4558) --- ...microprofile_openapi_MPOpenAPISupport.adoc | 2 +- .../io_helidon_openapi_OpenAPISupport.adoc | 2 +- .../io_helidon_openapi_SEOpenAPISupport.adoc | 2 +- docs/includes/attributes.adoc | 11 +- docs/includes/openapi.adoc | 203 ++++++++++++++++-- docs/mp/openapi.adoc | 120 +---------- docs/se/openapi.adoc | 136 +++++------- .../io/helidon/openapi/OpenAPISupport.java | 4 +- 8 files changed, 268 insertions(+), 212 deletions(-) diff --git a/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc index 80e4958fd77..fb58ad62ef3 100644 --- a/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc +++ b/docs/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc @@ -62,7 +62,7 @@ Optional configuration options: |`servers` |string[] |{nbsp} |Sets servers. |`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. |`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`static-file` |string |`META-INF/openapi.*` |Sets the file system path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`. |`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. |=== diff --git a/docs/config/io_helidon_openapi_OpenAPISupport.adoc b/docs/config/io_helidon_openapi_OpenAPISupport.adoc index c8603523223..dce936ee687 100644 --- a/docs/config/io_helidon_openapi_OpenAPISupport.adoc +++ b/docs/config/io_helidon_openapi_OpenAPISupport.adoc @@ -45,7 +45,7 @@ Optional configuration options: |key |type |default value |description |`cors` |xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assigns the CORS settings for the OpenAPI endpoint. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`static-file` |string |`META-INF/openapi.*` |Sets the file system path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`. |`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. |=== diff --git a/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc index 63983cf06cf..994995da3ec 100644 --- a/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc +++ b/docs/config/io_helidon_openapi_SEOpenAPISupport.adoc @@ -57,7 +57,7 @@ Optional configuration options: |`servers` |string[] |{nbsp} |Sets servers. |`servers.operation.*` |string[] |{nbsp} |Sets alternative servers to service the indicated operation (represented here by '*'). Repeat for multiple operations. |`servers.path.*` |string[] |{nbsp} |Sets alternative servers to service all operations at the indicated path (represented here by '*'). Repeat for multiple paths. -|`static-file` |string |`META-INF/openapi.(json|yaml|yml)` |Sets the file system path of the static OpenAPI document file. +|`static-file` |string |`META-INF/openapi.*` |Sets the file system path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`. |`web-context` |string |`/openapi` |Sets the web context path for the OpenAPI endpoint. |=== diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 8ba67dcd315..97b2891295e 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -82,9 +82,10 @@ endif::[] :microprofile-base-url: https://download.eclipse.org/microprofile -:microprofile-open-api-base-url: {microprofile-base-url}/microprofile-open-api-{version-lib-microprofile-metrics-api} -:microprofile-open-api-spec-url: {microprofile-open-api-base-url}/microprofile-openapi-spec-{version-lib-microprofile-metrics-api}.html -:microprofile-open-api-javadoc-url: {microprofile-open-api-base-url}/apidocs/org/eclipse/microprofile/openapi/ +:microprofile-open-api-base-url: {microprofile-base-url}/microprofile-open-api-{version-lib-microprofile-openapi-api} +:microprofile-open-api-spec-url: {microprofile-open-api-base-url}/microprofile-openapi-spec-{version-lib-microprofile-openapi-api}.html +:microprofile-open-api-javadoc-base-url: {microprofile-open-api-base-url}/apidocs +:microprofile-open-api-javadoc-url: {microprofile-open-api-javadoc-base-url}/apidocs/org/eclipse/microprofile/openapi/ :microprofile-lra-base-url: {microprofile-base-url}/microprofile-lra-{version-lib-microprofile-lra-api} :microprofile-lra-spec-url: {microprofile-lra-base-url}/microprofile-lra-spec-{version-lib-microprofile-lra-api}.html @@ -192,6 +193,7 @@ endif::[] :mp-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.cors :mp-tyrus-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.tyrus :mp-restclient-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.restclient +:openapi-javadoc-base-url: {javadoc-base-url}/io.helidon.openapi :reactive-base-url: {javadoc-base-url}/io.helidon.common.reactive :scheduling-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.scheduling :webclient-javadoc-base-url: {javadoc-base-url}/io.helidon.webclient @@ -209,6 +211,9 @@ endif::[] :jib-base-url: https://github.com/GoogleContainerTools/jib/blob/v{version-plugin-jib}-maven/jib-maven-plugin :jedis-base-url: {javadoc-io-base-url}/redis.clients/jedis/{version-lib-jedis}/redis/clients :kafka-client-base-url: https://kafka.apache.org/28/documentation.html +:openapi-version: 3.0.0 +:openapi-spec-base-url: https://github.com/OAI/OpenAPI-Specification/blob/master/versions +:openapi-spec-url: {openapi-spec-base-url}/{openapi-version}.md :oci-sdk-config-url: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm#SDK_and_CLI_Configuration_File :oci-database-url: https://docs.oracle.com/en-us/iaas/Content/Database/home.htm diff --git a/docs/includes/openapi.adoc b/docs/includes/openapi.adoc index f5e00ff6198..3fe63125925 100644 --- a/docs/includes/openapi.adoc +++ b/docs/includes/openapi.adoc @@ -18,16 +18,193 @@ ifndef::rootdir[:rootdir: {docdir}/..] -// tag::common-config[] -Helidon {h1-prefix} also supports additional properties specific to Helidon. - -.Helidon-specific OpenAPI Config Properties -|=== -|Property |Use - -|`openapi.web-context` |Path which serves the OpenAPI document (defaults to `/openapi`) -|`openapi.static-file` |Full path to the static OpenAPI file (defaults to -`META-INF/openapi.yml`, -`META-INF/openapi.yaml`, or -`META-INF/openapi.json`) -|=== +// tag::overview[] +The link:{openapi-spec-url}[OpenAPI specification] defines a standard way to express the interface exposed by a REST service. + +The link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] explains how MicroProfile embraces OpenAPI, adding annotations, configuration, and a service provider interface (SPI). + +ifdef::mp-flavor[Helidon {flavor-uc} implements the MicroProfile OpenAPI specification.] +ifdef::se-flavor[OpenAPI support in Helidon {flavor-uc} draws its inspiration from MicroProfile OpenAPI but does not implement the spec because Helidon {flavor-uc} does not support annotations.] + +The OpenAPI support in Helidon {flavor-uc} performs two main tasks: + +* Build an in-memory model of the REST API your service implements. +* Expose the model in text format (typically yaml) via the `/openapi` endpoint. + +To construct the model, Helidon gathers information about the service API from whichever of these sources are present in the application: + +* a _model reader_ ++ +The SPI defines an interface you can implement in your application for programmatically providing part or all of the model; +* a static OpenAPI document file packaged as part of your service; +ifdef::mp-flavor[] +* OpenAPI annotations; +endif::[] +* a _filter_ class ++ +The SPI defines an interface you can implement in your application which can mask parts of the model. + + +// end::overview[] + +// tag::furnish-openapi-info[] + +==== Furnish OpenAPI information about your endpoints +// It's a bit odd to intermix the SE and MP content in this common file this way. +// But I tried having a level 3 section in the SE file include a sequence of +// level 4 sections from here, and that led to errors with headers being out of sequence. +// With the entire level 3 section here and conditional text for SE and MP, AsciiDoctor is happy. +ifdef::se-flavor[] +OpenAPI support in Helidon SE largely follows the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec]. +But because Helidon SE does not process annotations, your application supplies data for the OpenAPI model in the other ways listed earlier. +endif::[] + +ifdef::mp-flavor[] +Helidon MP OpenAPI combines information from all of the following sources as it +builds its in-memory model of your application's API. It constructs the OpenAPI +document from this internal model. Your application can use one or more of these +techniques. + +===== Annotate the endpoints in your app +You can add MicroProfile OpenAPI annotations to the endpoints in your source code. +These annotations allow the Helidon MP OpenAPI runtime to discover the endpoints +and information about them via CDI at app start-up. + +Here is one of the endpoints, annotated for OpenAPI, from the example mentioned earlier: + +[source,java] +---- +@GET +@Operation(summary = "Returns a generic greeting", // <1> + description = "Greets the user generically") +@APIResponse(description = "Simple JSON containing the greeting", // <2> + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = GreetingMessage.class))) +@Produces(MediaType.APPLICATION_JSON) +public JsonObject getDefaultMessage() {...} +---- +<1> `@Operation` gives information about this endpoint. +<2> `@APIResponse` describes the HTTP response and declares its media type and contents. + +You can also define any request parameters the endpoint expects, although this +endpoint uses none. + +This excerpt shows only a few annotations for illustration. The +link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[Helidon MP OpenAPI example] illustrates more, +and the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] describes them all. + +===== Provide a static OpenAPI file +Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, +or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API +and they then generate an OpenAPI document file which you can include in your application +so OpenAPI can use it. + +===== Write and configure a model reader class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your +model reader code programmatically adds elements to the internal model that OpenAPI +builds. + +endif::[] + +===== Provide a static OpenAPI file +Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, +or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API +and they then generate an OpenAPI document file which you can include in your application +so OpenAPI can use it. + +===== Write and configure a model reader class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your +model reader code programmatically adds elements to the internal model that OpenAPI +builds. + +Change your application's MP configuration to set `mp.openapi.model.reader` as the +fully-qualified class name of your class. + +===== Write and configure a filter class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASFilter.html[`org.eclipse.microprofile.openapi.OASFilter`] interface. +As OpenAPI composes its internal model, it invokes your filter with each +model element _before_ adding the element to the model. Your filter can +accept the element as-is, modify it, or suppress it. + +Change your application's configuration to set `mp.openapi.filter` as the full-qualified +class name of your class. + +// end::furnish-openapi-info[] + +// tag::usage-access-endpoint[] +=== Accessing the REST Endpoint +Once you add the {flavor-uc} OpenAPI dependency to your +ifdef::mp-flavor[project,] +ifdef::se-flavor[project and add code to create the `OpenAPISupport` object to your routing,] +your application will automatically respond to the built-in endpoint -- +`/openapi` -- and it will return the OpenAPI document describing the endpoints +in your application. + +By default, per the MicroProfile OpenAPI spec, the default format of the OpenAPI document is YAML. +There is not yet an adopted IANA YAML media type, but a proposed one specifically +for OpenAPI documents that has some support is `application/vnd.oai.openapi`. +That is what Helidon returns, by default. + +In addition, a client can specify the HTTP header `Accept` as either `application/vnd.oai.openapi+json` or +`application/json` to request JSON. Alternatively, the client can pass the query parameter `format` as either `JSON` +or `YAML` to receive `application/json` or `application/vnd.oai.openapi` (YAML) output, respectively. +// end::usage-access-endpoint[] + +// tag::api[] +ifdef::mp-flavor[] +The link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI specification] gives a listing and brief examples of the annotations you can add to your code to convey OpenAPI information. +endif::[] + +The link:{microprofile-open-api-javadoc-base-url}[MicroProfile OpenAPI JavaDocs] give full details of the +ifdef::mp-flavor[annotations and the other] +classes and interfaces you can use in your code. +ifdef::se-flavor[] +Remember that, although the JavaDocs describe annotations, Helidon {flavor-uc} does not support them. +endif::[] + +// end::api[] + + +// tag::additional-building-jandex[] + +=== Building the Jandex index + +A Jandex index stores information about the classes and methods in your app and +what annotations they have. It allows CDI to process annotations faster during your +application's start-up. + +Add the link:https://github.com/wildfly/jandex-maven-plugin[Jandex maven plug-in] to the `` +section of your `pom.xml`: + +[source,xml,subs="attributes+"] +---- + + org.jboss.jandex + jandex-maven-plugin + {jandex-plugin-version} + + + make-index + + jandex + + + + +---- +When you build your app `maven` should include the index `META-INF/jandex.idx` in +the JAR. + +[NOTE] +==== +If you _do not_ modify your build to create +the index then the Helidon MP OpenAPI runtime automatically creates one in memory during +app start-up. This slows down your app start-up and, depending on how CDI is +configured, might inadvertently miss information. + +We _strongly recommend_ using the Jandex plug-in to build the index into your app. +==== +// end::additional-building-jandex[] \ No newline at end of file diff --git a/docs/mp/openapi.adoc b/docs/mp/openapi.adoc index 4ebce5249b4..9b992c251f5 100644 --- a/docs/mp/openapi.adoc +++ b/docs/mp/openapi.adoc @@ -27,7 +27,7 @@ include::{rootdir}/includes/mp.adoc[] -== ToC +== Contents - <> - <> @@ -39,8 +39,7 @@ include::{rootdir}/includes/mp.adoc[] == Overview -Easily allow your Helidon MP application to serve an OpenAPI document -that describes your application's endpoints. +include::{rootdir}/includes/openapi.adoc[tag=overview] include::{rootdir}/includes/dependencies.adoc[] @@ -82,65 +81,7 @@ To use OpenAPI from your Helidon MP app: 2. Furnish OpenAPI information about your application's endpoints. 3. Update your application's configuration (optional). - -==== Furnish OpenAPI information about your endpoints -Helidon MP OpenAPI combines information from all of the following sources as it -builds its in-memory model of your application's API. It constructs the OpenAPI -document from this internal model. Your application can use one or more of these -techniques. - -===== Annotate the endpoints in your app -You can add MicroProfile OpenAPI annotations to the endpoints in your source code. -These annotations allow the Helidon MP OpenAPI runtime to discover the endpoints -and information about them via CDI at app start-up. - -Here is one of the endpoints, annotated for OpenAPI, from the example mentioned earlier: - -[source,java] ----- -@GET -@Operation(summary = "Returns a generic greeting", // <1> - description = "Greets the user generically") -@APIResponse(description = "Simple JSON containing the greeting", // <2> - content = @Content(mediaType = "application/json", - schema = @Schema(implementation = GreetingMessage.class))) -@Produces(MediaType.APPLICATION_JSON) -public JsonObject getDefaultMessage() {...} ----- -<1> `@Operation` gives information about this endpoint. -<2> `@APIResponse` describes the HTTP response and declares its media type and contents. - -You can also define any request parameters the endpoint expects, although this -endpoint uses none. - -This excerpt shows only a few annotations for illustration. The -link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[Helidon MP OpenAPI example] illustrates more, -and the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] describes them all. - -===== Provide a static OpenAPI file -Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, -or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API -and they then generate an OpenAPI document file which you can include in your application -so OpenAPI can use it. - -===== Write and configure a model reader class -Write a Java class that implements the OpenAPI -link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your -model reader code programmatically adds elements to the internal model that OpenAPI -builds. - -Change your application's MP configuration to set `mp.openapi.model.reader` as the -fully-qualified class name of your class. - -===== Write and configure a filter class -Write a Java class that implements the OpenAPI -link:{microprofile-open-api-javadoc-url}/OASFilter.html[`org.eclipse.microprofile.openapi.OASFilter`] interface. -As OpenAPI composes its internal model, it invokes your filter with each -model element _before_ adding the element to the model. Your filter can -accept the element as-is, modify it, or suppress it. - -Change your application's configuration to set `mp.openapi.filter` as the full-qualified -class name of your class. +include::{rootdir}/includes/openapi.adoc[tag=furnish-openapi-info] === Update your application configuration Beyond the two config properties that denote the model reader and filter, Helidon @@ -148,21 +89,11 @@ MP OpenAPI supports a number of other mandated settings. These are described in link:{microprofile-open-api-spec-url}#configuration[configuration section] of the MicroProfile OpenAPI spec. +include::{rootdir}/includes/openapi.adoc[tag=usage-access-endpoint] -== REST Endpoint +== API -The Helidon MP application will automatically respond to the built-in endpoint -- -`/openapi` -- and it will return the OpenAPI document describing the endpoints -in your application. - -By default, per the MicroProfile OpenAPI spec, the default format of the OpenAPI document is YAML. -There is not yet an adopted IANA YAML media type, but a proposed one specifically -for OpenAPI documents that has some support is `application/vnd.oai.openapi`. -That is what Helidon returns, by default. - -In addition, a client can specify the HTTP header `Accept:` as either `application/vnd.oai.openapi+json` or - `application/json` to request JSON. Alternatively, the client can pass the query parameter `format` as either `JSON` - or `YAML` to receive `application/json` or `application/vnd.oai.openapi` (YAML) output, respectively. +include::{rootdir}/includes/openapi.adoc[tag=api] == Configuration @@ -336,44 +267,7 @@ Full example is available link:{helidon-github-tree-url}}/examples/microprofile/ == Additional Information - -=== Building the Jandex index - -A Jandex index stores information about the classes and methods in your app and -what annotations they have. It allows CDI to process annotations faster during your -application's start-up. - -Add the link:https://github.com/wildfly/jandex-maven-plugin[Jandex maven plug-in] to the `` -section of your `pom.xml`: - -[source,xml,subs="attributes+"] ----- - - org.jboss.jandex - jandex-maven-plugin - {jandex-plugin-version} - - - make-index - - jandex - - - - ----- -When you build your app `maven` should include the index `META-INF/jandex.idx` in -the JAR. - -[NOTE] -==== -If you _do not_ modify your build to create -the index then the Helidon MP OpenAPI runtime automatically creates one in memory during -app start-up. This slows down your app start-up and, depending on how CDI is -configured, might inadvertently miss information. - -We _strongly recommend_ using the Jandex plug-in to build the index into your app. -==== +include::{rootdir}/includes/openapi.adoc[tag=additional-building-jandex] == Reference diff --git a/docs/se/openapi.adoc b/docs/se/openapi.adoc index 7bbd7706ded..a8abef06bb3 100644 --- a/docs/se/openapi.adoc +++ b/docs/se/openapi.adoc @@ -26,8 +26,19 @@ include::{rootdir}/includes/se.adoc[] -Easily allow your Helidon SE application to serve an OpenAPI document -that describes your application's endpoints. +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +include::{rootdir}/includes/openapi.adoc[tag=overview] include::{rootdir}/includes/dependencies.adoc[] @@ -39,80 +50,19 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== OpenAPI support in Helidon SE +== Usage You can very simply add support for OpenAPI to your Helidon SE application. This document shows what changes you need to make to your application and how to access the OpenAPI document for your application at runtime. -== Changing your application +=== Changing your application -OpenAPI support in Helidon SE largely follows the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec]. -But Helidon SE does not process annotations, which is one way to convey OpenAPI -information about the endpoints in your app. You can still use OpenAPI with your -Helidon SE app by providing OpenAPI information about the endpoints without using annotations. +==== Register `OpenAPISupport` in your application routing -Helidon SE includes a link:{helidon-github-tree-url}/examples/openapi[complete OpenAPI example] -based on the SE quick-start sample app. +Helidon SE provides the link:{openapi-javadoc-base-url}/OpenAPISupport.html[`OpenAPISupport`] class which your application uses to assemble the in-memory model and expose the `/openapi` endpoint to clients. You can create an instance either using a static `create` method or by instantiating its link:{openapi-javadoc-base-url}/OpenAPISupport.Builder.html[`Builder`]. The xref:#register_openapisupport[example below] illustrates one way to do this. -To use OpenAPI from your Helidon SE app: - -1. Edit your `pom.xml`. -2. Update your Java code to register `OpenAPISupport`. -3. Furnish OpenAPI information about your application's endpoints. -4. Update your application's Helidon configuration (optional). - -=== Edit your `pom.xml` -Add a dependency for <> runtime support. -This is a compile-time dependency, because your code must register -`OpenAPISupport` (a class in that artifact) like this: - -=== Register `OpenAPISupport` in your Java code -[source,java] ----- -Config config = Config.create(); -return Routing.builder() - .register(JsonSupport.create()) - .register(OpenAPISupport.create(config)) // <1> - .register(health) // Health at "/health" - .register(metrics) // Metrics at "/metrics" - .register("/greet", greetService) - .build(); ----- -<1> Adds the `OpenAPISupport` service to your server. - -=== Furnish OpenAPI information about your endpoints -Helidon SE OpenAPI combines information from all of the following sources as it -builds its in-memory model of your application's API. It constructs the OpenAPI -document from this internal model. Your application can use one or more of -these techniques. - -==== Provide a static OpenAPI file -Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, -or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API -and they then generate an OpenAPI document file which you can include in your application -so OpenAPI can use it. - -==== Write and configure a model reader class -Write a Java class that implements the OpenAPI -link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your -model reader code programmatically adds elements to the internal model that OpenAPI -builds. - -Change your application's configuration to set `openapi.model.reader` as the -fully-qualified class name of your class. Also see -<> below. - -==== Write and configure a filter class -Write a Java class that implements the OpenAPI -link:{microprofile-open-api-javadoc-url}/OASFilter.html[`org.eclipse.microprofile.openapi.OASFilter`] interface. -As OpenAPI composes its internal model, it invokes your filter with each -model element _before_ adding the element to the model. Your filter can -accept the element as-is, modify it, or suppress it. - -Change your application's configuration to set `openapi.filter` as the full-qualified -class name of your class. Also see -<> below. +include::{rootdir}/includes/openapi.adoc[tag=furnish-openapi-info] ==== Add OpenAPI dependency If you implement either a model reader or a filter, add this dependency to your @@ -127,6 +77,20 @@ If you implement either a model reader or a filter, add this dependency to your ---- +include::{rootdir}/includes/openapi.adoc[tag=usage-access-endpoint] + +== API + +include::{rootdir}/includes/openapi.adoc[tag=api] + +Helidon {flavor-uc} provides an API for creating and setting up the REST endpoint which serves OpenAPI documents to clients at the `/openapi` path. Use either static methods on link:{openapi-javadoc-base-url}/OpenAPISupport.html[`OpenAPISupport`] or use its link:{openapi-javadoc-base-url}/OpenAPISupport.Builder.html[`Builder`] to create an instance of `OpenAPISupport`. Then add that instance to your application's routing. The <<#register_openapisupport,example>> below shows how to do this. + +== Configuration + +Helidon SE OpenAPI configuration supports the following settings: + +include::{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[leveloffset=+1,tag=config] + === Update application configuration Helidon SE support for OpenAPI supports a handful of config properties patterned after those described in the MicroProfile OpenAPI spec, two of which were mentioned above. @@ -153,15 +117,31 @@ Set these config properties in one of the config sources your app uses so the Helidon config system will load them. Often developers use `application.yaml` at the top level of the application JAR. -== Accessing the OpenAPI document -Now your Helidon SE application will automatically respond to an additional endpoint -- - `/openapi` -- and it will return the OpenAPI document describing the endpoints -in your application. +== Examples + +Helidon SE provides a link:{helidon-github-tree-url}/examples/openapi[complete OpenAPI example] +based on the SE QuickStart sample app which includes a model reader and a filter. + +Most Helidon {flavor-uc} applications need only to create and register `OpenAPISupport`. + +[#register_openapisupport] +=== Register `OpenAPISupport` + +.Java Code to Register `OpenAPISupport` for Routing +[source,java] +---- +Config config = Config.create(); +return Routing.builder() + .register(JsonSupport.create()) + .register(OpenAPISupport.create(config)) // <1> + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics" + .register("/greet", greetService) + .build(); +---- +<1> Adds the `OpenAPISupport` service to your server. -By default, Helidon OpenAPI returns the OpenAPI document in YAML. -There is not yet an adopted IANA YAML media type, but a proposed one specifically -for OpenAPI documents that has some support is `application/vnd.oai.openapi`. -That is what Helidon returns, by default. +If you need more control over the `OpenAPISupport` instance, invoke `OpenAPISupport.builder()` to get an `OpenAPISupport.Builder` object and work with it. -In addition a client can specify the HTTP header `Accept:` as either `application/vnd.oai.openapi+json` or `application/json` -to request JSON. Alternatively, the client can pass the query parameter `format` as either `JSON` or `YAML` to receive `application/json` or `application/vnd.oai.openapi` (YAML) output, respectively. +== Additional Information +include::{rootdir}/includes/openapi.adoc[tag=additional-building-jandex] \ No newline at end of file diff --git a/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java b/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java index c4502c94678..6ebcb9d4c80 100644 --- a/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java +++ b/openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java @@ -820,12 +820,12 @@ public B webContext(String path) { } /** - * Sets the file system path of the static OpenAPI document file. + * Sets the file system path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`. * * @param path non-null location of the static OpenAPI document file * @return updated builder instance */ - @ConfiguredOption(DEFAULT_STATIC_FILE_PATH_PREFIX + "(" + OpenAPIMediaType.TYPE_LIST + ")") + @ConfiguredOption(value = DEFAULT_STATIC_FILE_PATH_PREFIX + "*") public B staticFile(String path) { Objects.requireNonNull(path, "path to static file must be non-null"); staticFilePath = Optional.of(path); From 2ec40b3a86b0581203f3eb1e763fe97364c53ea8 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 15 Jul 2022 19:56:38 -0500 Subject: [PATCH 31/51] Remove repeated content (#4560) --- docs/se/openapi.adoc | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/docs/se/openapi.adoc b/docs/se/openapi.adoc index a8abef06bb3..11d493551e8 100644 --- a/docs/se/openapi.adoc +++ b/docs/se/openapi.adoc @@ -91,31 +91,7 @@ Helidon SE OpenAPI configuration supports the following settings: include::{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[leveloffset=+1,tag=config] -=== Update application configuration -Helidon SE support for OpenAPI supports a handful of config properties patterned after -those described in the MicroProfile OpenAPI spec, two of which were mentioned above. - -[[se_config]] -.Helidon SE OpenAPI Config Properties -|=== -|Property |Use - -|`openapi.model.reader` |Fully-qualified class name for the model reader -|`openapi.filter` |Fully-qualified class name for the filter -|`openapi.servers` |Lists servers that provide connectivity information -|`openapi.servers.path` |Prefix for config properties specifying alternative -servers for given paths -|`openapi.servers.operation` |Prefix for config properties specifying alternative -servers for given operations -|`openapi.schema` |Prefix for config properties defining the schema for a class -|=== -For more information on what these settings do consult the MicroProfile OpenAPI spec. - -include::{rootdir}/includes/openapi.adoc[common-config] - -Set these config properties in one of the config sources your app uses so the -Helidon config system will load them. Often developers use `application.yaml` at the -top level of the application JAR. + == Examples From b221e93a42ef06ecb905d6961294eb2f415d66c5 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Sat, 16 Jul 2022 16:02:14 +0300 Subject: [PATCH 32/51] [New Doc PR] - Helidon MP LRA #4205 (#4513) --- ..._helidon_microprofile_lra_Coordinator.adoc | 49 ++ docs/mp/introduction/introduction.adoc | 3 +- docs/mp/lra.adoc | 460 ++++++++++++++++++ docs/mp/lra/coordinator.adoc | 84 ---- docs/mp/lra/introduction.adoc | 139 ------ docs/mp/lra/participant.adoc | 272 ----------- docs/sitegen.yaml | 5 +- 7 files changed, 511 insertions(+), 501 deletions(-) create mode 100644 docs/config/io_helidon_microprofile_lra_Coordinator.adoc create mode 100644 docs/mp/lra.adoc delete mode 100644 docs/mp/lra/coordinator.adoc delete mode 100644 docs/mp/lra/introduction.adoc delete mode 100644 docs/mp/lra/participant.adoc diff --git a/docs/config/io_helidon_microprofile_lra_Coordinator.adoc b/docs/config/io_helidon_microprofile_lra_Coordinator.adoc new file mode 100644 index 00000000000..522009c0e9e --- /dev/null +++ b/docs/config/io_helidon_microprofile_lra_Coordinator.adoc @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +//MANUALLY CREATED DOCUMENT + +:description: Configuration of io_helidon_microprofile_lra_Coordinator +:keywords: helidon, io_helidon_microprofile_lra_Coordinator +:basic-table-intro: The table below lists the configuration keys that configure io_helidon_microprofile_lra_Coordinator + += LRA Configuration + +// tag::config[] + +[source,text] +.Type +---- +io.helidon.microprofile.lra +---- + +Optional configuration options: +[cols="3,3,2,5"] + +|=== +|Key |Type |Default value |Description + +|`mp.lra.coordinator.url` |string |`http://localhost:8070/lra-coordinator` |Url of coordinator. +|`mp.lra.coordinator.propagation.active` |boolean |{nbsp} |Propagate LRA headers LRA_HTTP_CONTEXT_HEADER and LRA_HTTP_PARENT_CONTEXT_HEADER through non-LRA endpoints. +|`mp.lara.participant.url` |string |{nbsp} |Url of the LRA enabled service overrides standard base uri, so coordinator can call load-balancer instead of the service. +|`mp.lra.coordinator.timeout` |string |{nbsp} |Timeout for synchronous communication with coordinator. +|`mp.lra.coordinator.timeout-unit` |string |{nbsp} |Timeout unit for synchronous communication with coordinator. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index e9784f6c8b8..d1c818c0c6a 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -142,7 +142,7 @@ Defines a compact and self-contained way for securely transmitting information b //LRA [CARD] .Long Running Actions -[icon=pending_actions,link=../lra/introduction.adoc] +[icon=pending_actions,link=../lra.adoc] -- Distributed transactions for microservices following SAGA pattern. -- @@ -242,4 +242,3 @@ Follow step-by-step guides to build your applications using Helidon MP. Browse the Helidon Javadocs. -- ==== - diff --git a/docs/mp/lra.adoc b/docs/mp/lra.adoc new file mode 100644 index 00000000000..d3cc3c6937c --- /dev/null +++ b/docs/mp/lra.adoc @@ -0,0 +1,460 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 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. + +/////////////////////////////////////////////////////////////////////////////// + += LRA +:toc: +:toc-placement: preamble +:description: Long Running Actions +:keywords: helidon, mp, lra +:h1Prefix: MP +:feature-name: Long Running Actions +:spec-version: 1.0-RC3 +:spec-name: Micro Profile {feature-name} specification +:javadoc-link: https://download.eclipse.org/microprofile/microprofile-lra-{spec-version}/apidocs/org/eclipse/microprofile/lra/annotation/ +:rootdir: {docdir}/.. + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Distributed transactions for microservices are known as SAGA design patterns and are defined by the {microprofile-lra-spec-url}[{spec-name}]. +Unlike well known XA protocol, LRA is asynchronous and therefore much more scalable. Every LRA JAX-RS resource (<>) defines endpoints to be invoked when transaction needs to be _completed_ or _compensated_. + + +include::{rootdir}/includes/dependencies.adoc[] +[source,xml] +---- + + io.helidon.microprofile.lra + helidon-microprofile-lra + + + + io.helidon.lra + helidon-lra-coordinator-narayana-client + +---- + +== Usage + +LRA transactions need to be coordinated over REST API by the LRA coordinator. <> +keeps track of all transactions and calls the `@Compensate` or `@Complete` endpoints for all participants involved in the particular +transaction. LRA transaction is first started, then joined by <>. +Participant reports the successful finish of transaction by calling complete. Coordinator then calls the JAX-RS +_complete_ endpoint that was registered during the join of each +<>. As the completed or compensated participants don't have to be on same instance, +the whole architecture is highly scalable. + +image::lra/lra-complete-lb.svg[Complete] + +In case of error during the LRA transaction, participant reports cancel of LRA to coordinator. +<> calls compensate on all the joined participants. + +image::lra/lra-compensate-lb-error.svg[Cancel] + +When participant joins the LRA with timeout defined `@LRA(value = LRA.Type.REQUIRES_NEW, timeLimit = 5, timeUnit = ChronoUnit.MINUTES)`, coordinator compensate if timeout occurs before close is reported by participants. + +image::lra/lra-compensate-lb-timeout.svg[Timeout] + +== API + +=== Participant + +The Participant, or Compensator, is an LRA resource with at least one of the JAX-RS(or non-JAX-RS) methods annotated with +{javadoc-link}Compensate.html[@Compensate] or {javadoc-link}AfterLRA.html[@AfterLRA]. + + +=== @LRA [[lra-method]] + +{javadoc-link}ws/rs/LRA.html[~javadoc~] + +Marks JAX-RS method which should run in LRA context and needs to be accompanied by at least minimal set of mandatory +participant methods(<> or <>). + +LRA options: + +* {javadoc-link}ws/rs/LRA.html#value--[value] +** {javadoc-link}ws/rs/LRA.Type.html#REQUIRED[REQUIRED] join incoming LRA or create and join new +** {javadoc-link}ws/rs/LRA.Type.html#REQUIRES_NEW[REQUIRES_NEW] create and join new LRA +** {javadoc-link}ws/rs/LRA.Type.html#MANDATORY[MANDATORY] join incoming LRA or fail +** {javadoc-link}ws/rs/LRA.Type.html#SUPPORTS[SUPPORTS] join incoming LRA or continue outside LRA context +** {javadoc-link}ws/rs/LRA.Type.html#NOT_SUPPORTED[NOT_SUPPORTED] always continue outside LRA context +** {javadoc-link}ws/rs/LRA.Type.html#NEVER[NEVER] Fail with 412 if executed in LRA context +** {javadoc-link}ws/rs/LRA.Type.html#NESTED[NESTED] create and join new LRA nested in the incoming LRA context +* {javadoc-link}ws/rs/LRA.html#timeLimit--[timeLimit] max time limit before LRA gets cancelled automatically by <> +* {javadoc-link}ws/rs/LRA.html#timeUnit--[timeUnit] time unit if the timeLimit value +* {javadoc-link}ws/rs/LRA.html#end--[end] when false LRA is not closed after successful method execution +* {javadoc-link}ws/rs/LRA.html#cancelOn--[cancelOn] which HTTP response codes of the method causes LRA to cancel +* {javadoc-link}ws/rs/LRA.html#cancelOnFamily--[cancelOnFamily] which family of HTTP response codes causes LRA to cancel + + +Method parameters: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction + +[source,java] +---- +@PUT +@LRA(LRA.Type.REQUIRES_NEW, timeLimit = 500, timeUnit = ChronoUnit.MILLIS) +@Path("start-example") +public Response startLra(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, String data) +---- + +=== @Compensate [[compensate-participant-method]] + +{javadoc-link}Compensate.html[~javadoc~] + +WARNING: Expected to be called by LRA <> only! + +Compensate method is called by <> when LRA is cancelled, +usually by error during execution of method body of <>. +If the method responds with 500 or 202, coordinator will eventually try the call again. +If participant has <>, <> +retrieves the status to find out if retry should be done. + + +==== JAX-RS variant with supported LRA context values: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA + +[source,java] +---- +@PUT +@Path("/compensate") +@Compensate +public Response compensateWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, + @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parent){ + return LRAResponse.compensated(); +} +---- + +==== Non JAX-RS variant with supported LRA context values: + +* URI with LRA id + +[source,java] +---- +@Compensate +public void compensate(URI lraId) +---- + +=== @Complete [[complete-participant-method]] + +{javadoc-link}Complete.html[~javadoc~] + +WARNING: Expected to be called by LRA <> only! + +Complete method is called by <> when LRA is successfully closed. +If the method responds with 500 or 202, coordinator will eventually try the call again. +If participant has <>, <> retrieves the status to find out if retry should be done. + +==== JAX-RS variant with supported LRA context values: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA + +[source,java] +---- +@PUT +@Path("/complete") +@Complete +public Response complete(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, + @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parentLraId) +---- + +==== Non JAX-RS variant with supported LRA context values: + +* URI with LRA id + +[source,java] +---- +@Complete +public void complete(URI lraId) +---- + +=== @Forget + +{javadoc-link}Forget.html[~javadoc~] + +WARNING: Expected to be called by LRA <> only! + +<> and <> +methods can fail(500) or report that compensation/completion is in progress(202). +In such case participant needs to be prepared to report its status over <> +to <>. +When <> decides all the participants have finished, method annotated with @Forget is called. + +==== JAX-RS variant with supported LRA context values: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA + +[source,java] +---- +@DELETE +@Path("/forget") +@Forget +public Response forget(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, + @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parent) +---- + +==== Non JAX-RS variant with supported LRA context values: + +* URI with LRA id + +[source,java] +---- +@Forget +public void forget(URI lraId) +} +---- + +=== @Leave + +{javadoc-link}ws/rs/Leave.html[~javadoc~] + +Method annotated with @Leave called with LRA context(with header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER]) informs <> that current participant is leaving the LRA. +Method body is executed after leave signal is sent. +As a result, participant methods complete and compensate won't be called when the particular LRA ends. + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction + +[source,java] +---- +@PUT +@Path("/leave") +@Leave +public Response leaveLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraIdtoLeave) +---- + +=== @Status [[status-participant-method]] + +{javadoc-link}Status.html[~javadoc~] + +WARNING: Expected to be called by LRA <> only! + +If the coordinator's call to the participant's method fails, then it will retry the call. +If the participant is not idempotent, then it may need to report its state to coordinator by declaring method +annotated with @Status for reporting if previous call did change participant status. +<> can call it and decide if compensate or complete retry is needed. + +==== JAX-RS variant with supported LRA context values: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction +* {javadoc-link}ParticipantStatus.html[ParticipantStatus] - Status of the participant reported to <> + +[source,java] +---- +@GET +@Path("/status") +@Status +public Response reportStatus(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { + return Response.status(ParticipantStatus.FailedToCompensate).build(); +} +---- + +==== Non JAX-RS variant with supported LRA context values: + +* URI with LRA id +* {javadoc-link}ParticipantStatus.html[ParticipantStatus] - Status of the participant reported to <> + +[source,java] +---- +@Status +public Response reportStatus(URI lraId){ + return Response.ok(ParticipantStatus.FailedToCompensate).build(); +} +---- + +=== @AfterLRA [[after-participant-method]] + +{javadoc-link}AfterLRA.html[~javadoc~] + +WARNING: Expected to be called by LRA <> only! + +Method annotated with {javadoc-link}AfterLRA.html[@AfterLRA] in the same class as the one with @LRA annotation gets invoked after particular LRA finishes. + +==== JAX-RS variant with supported LRA context values: + +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_ENDED_CONTEXT_HEADER[LRA_HTTP_ENDED_CONTEXT_HEADER] - id of the finished LRA transaction +* Header {javadoc-link}ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA +* {javadoc-link}LRAStatus.html[LRAStatus] - Final status of the LRA ({javadoc-link}LRAStatus.html#Cancelled[Cancelled], {javadoc-link}LRAStatus.html#Closed[Closed], {javadoc-link}LRAStatus.html#FailedToCancel[FailedToCancel], {javadoc-link}LRAStatus.html#FailedToClose[FailedToClose]) + +[source,java] +---- +@PUT +@Path("/finished") +@AfterLRA +public Response whenLRAFinishes(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId, + @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parentLraId, + LRAStatus status) +---- + +==== Non JAX-RS variant with supported LRA context values: + +* URI with finished LRA id +* {javadoc-link}LRAStatus.html[LRAStatus] - Final status of the LRA ({javadoc-link}LRAStatus.html#Cancelled[Cancelled], {javadoc-link}LRAStatus.html#Closed[Closed], {javadoc-link}LRAStatus.html#FailedToCancel[FailedToCancel], {javadoc-link}LRAStatus.html#FailedToClose[FailedToClose]) + +[source,java] +---- +public void whenLRAFinishes(URI lraId, LRAStatus status) +---- + +== Configuration + +include::{rootdir}/config/io_helidon_microprofile_lra_Coordinator.adoc[leveloffset=+1,tag=config] + +[source,yaml] +.Example of LRA configuration +---- +mp.lra: + coordinator.url: http://localhost:8070/lra-coordinator <1> + propagation.active: true <2> + participant.url: http://coordinator.visible.host:80/awesomeapp <3> +---- +<1> Url of coordinator +<2> Propagate LRA headers LRA_HTTP_CONTEXT_HEADER and LRA_HTTP_PARENT_CONTEXT_HEADER through non-LRA endpoints +<3> Url of the LRA enabled service overrides standard base uri, +so coordinator can call load-balancer instead of the service + +For more information continue to {microprofile-lra-spec-url}[{spec-name}]. + +== Examples + +The following example shows how a simple LRA participant starts and joins a transaction after calling the '/start-example' resource. +When startExample method finishes successfully, close is reported to <> +and `/complete-example` endpoint is called by coordinator to confirm successful closure of the LRA. + +If an exception occurs during startExample method execution, coordinator receives cancel call and `/compensate-example` +is called by coordinator to compensate for cancelled LRA transaction. + +[source,java] +.Example of simple LRA participant +---- +@PUT +@LRA(LRA.Type.REQUIRES_NEW) <1> +@Path("start-example") +public Response startExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, <2> + String data) { + if (data.contains("BOOM")) { + throw new RuntimeException("BOOM 💥"); <3> + } + + LOGGER.info("Data " + data + " processed 🏭"); + return Response.ok().build(); <4> +} + +@PUT +@Complete <5> +@Path("complete-example") +public Response completeExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { + LOGGER.log(Level.INFO, "LRA id: {0} completed 🎉", lraId); + return LRAResponse.completed(); +} + +@PUT +@Compensate <6> +@Path("compensate-example") +public Response compensateExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { + LOGGER.log(Level.SEVERE, "LRA id: {0} compensated 🦺", lraId); + return LRAResponse.compensated(); +} +---- +<1> This JAX-RS PUT method will start new LRA transactions and join it before method body gets executed +<2> LRA id assigned by coordinator to this LRA transaction +<3> When method execution finishes exceptionally, cancel signal for this particular LRA is sent to coordinator +<4> When method execution finishes successfully, complete signal for this particular LRA is sent to coordinator +<5> Method which will be called by coordinator when LRA is completed +<6> Method which will be called by coordinator when LRA is canceled + +== Additional Information + +=== Coordinator +Coordinator is a service that tracks all LRA transactions and calls the compensate REST endpoints of +the participants when the LRA transaction gets cancelled or completes (in case it gets closed). +In addition, participant also keeps track of timeouts, retries participant calls, and assigns LRA ids. + +.Helidon LRA supports following coordinators +* Helidon LRA coordinator +* https://narayana.io/lra[Narayana coordinator]. + +=== Helidon LRA coordinator + +CAUTION: Experimental tool, usage in production is not advised. + +[source,bash] +.Build and run Helidon LRA coordinator +---- +docker build -t helidon/lra-coordinator https://github.com/oracle/helidon.git#:lra/coordinator/server +docker run -dp 8070:8070 --name lra-coordinator --network="host" helidon/lra-coordinator +---- + +Helidon LRA coordinator is compatible with Narayana clients, you need to add an additional dependency for Narayana client: +[source,xml] +.Dependency needed for using Helidon LRA with Narayana compatible coordinator +---- + + io.helidon.lra + helidon-lra-coordinator-narayana-client + +---- + +=== Narayana +https://narayana.io[Narayana] is a transaction manager supporting LRA. +To use Narayana LRA coordinator with Helidon LRA client you need to add an additional dependency for Narayana client: + +[source,xml] +.Dependency needed for using Helidon LRA with Narayana coordinator +---- + + io.helidon.lra + helidon-lra-coordinator-narayana-client + +---- + +The simplest way to run Narayana LRA coordinator locally: + +[source,bash] +.Downloading and running Narayana LRA coordinator +---- +wget https://search.maven.org/remotecontent?filepath=org/jboss/narayana/rts/lra-coordinator-quarkus/5.11.1.Final/lra-coordinator-quarkus-5.11.1.Final-runner.jar \ +-O narayana-coordinator.jar \ +&& java -Dquarkus.http.port=8070 -jar narayana-coordinator.jar +---- + +Narayana LRA coordinator is running by default under `lra-coordinator` context, +with port `8070` defined in the snippet above you need to configure your Helidon LRA app as follows: +`mp.lra.coordinator.url=http://localhost:8070/lra-coordinator` + + +== Reference + +* https://github.com/eclipse/microprofile-lra[MicroProfile LRA GitHub Repository] +* {microprofile-lra-spec-url}[{spec-name}] +* https://download.eclipse.org/microprofile/microprofile-lra-{spec-version}/apidocs/org/eclipse/microprofile/lra/[Microprofile LRA JavaDoc] +* https://helidon.io/docs/v2/apidocs/io.helidon.lra.coordinator.client/module-summary.html[Helidon LRA Client JavaDoc] \ No newline at end of file diff --git a/docs/mp/lra/coordinator.adoc b/docs/mp/lra/coordinator.adoc deleted file mode 100644 index d902de54420..00000000000 --- a/docs/mp/lra/coordinator.adoc +++ /dev/null @@ -1,84 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Coordinator -:toc: -:toc-placement: preamble -:description: Long Running Actions -:keywords: helidon, mp, lra -:feature-name: Long Running Actions -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -== Coordinator -Coordinator is a service that tracks all LRA transactions and calls the compensate REST endpoints of -the participants when the LRA transaction gets cancelled or completes (in case it gets closed). -In addition, participant also keeps track of timeouts, retries participant calls, and assigns LRA ids. - -.Helidon LRA supports following coordinators -* Helidon LRA coordinator -* https://narayana.io/lra[Narayana coordinator]. - -=== Helidon LRA coordinator - -CAUTION: Experimental tool, usage in production is not advised. - -[source,bash] -.Build and run Helidon LRA coordinator ----- -docker build -t helidon/lra-coordinator https://github.com/oracle/helidon.git#:lra/coordinator/server -docker run -dp 8070:8070 --name lra-coordinator --network="host" helidon/lra-coordinator ----- - -Helidon LRA coordinator is compatible with Narayana clients, you need to add an additional dependency for Narayana client: -[source,xml] -.Dependency needed for using Helidon LRA with Narayana compatible coordinator ----- - - io.helidon.lra - helidon-lra-coordinator-narayana-client - ----- - -=== Narayana -https://narayana.io[Narayana] is a transaction manager supporting LRA. -To use Narayana LRA coordinator with Helidon LRA client you need to add an additional dependency for Narayana client: - -[source,xml] -.Dependency needed for using Helidon LRA with Narayana coordinator ----- - - io.helidon.lra - helidon-lra-coordinator-narayana-client - ----- - -The simplest way to run Narayana LRA coordinator locally: - -[source,bash] -.Downloading and running Narayana LRA coordinator ----- -curl -o narayana-coordinator.jar \ - https://repo1.maven.org/maven2/org/jboss/narayana/rts/lra-coordinator-quarkus/5.11.1.Final/lra-coordinator-quarkus-5.11.1.Final-runner.jar && \ -java -Dquarkus.http.port=8070 -jar narayana-coordinator.jar ----- - -Narayana LRA coordinator is running by default under `lra-coordinator` context, -with port `8070` defined in the snippet above you need to configure your Helidon LRA app as follows: -`mp.lra.coordinator.url=http://localhost:8070/lra-coordinator` \ No newline at end of file diff --git a/docs/mp/lra/introduction.adoc b/docs/mp/lra/introduction.adoc deleted file mode 100644 index 3882b29a5f9..00000000000 --- a/docs/mp/lra/introduction.adoc +++ /dev/null @@ -1,139 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= LRA -:toc: -:toc-placement: preamble -:description: Long Running Actions -:keywords: helidon, mp, lra -:feature-name: Long Running Actions -:rootdir: {docdir}/../.. -:imagesdir: {rootdir}/images - -include::{rootdir}/includes/mp.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - - io.helidon.microprofile.lra - helidon-microprofile-lra - - - - io.helidon.lra - helidon-lra-coordinator-narayana-client - - ----- - -== {feature-name} (LRA) -Distributed transactions for microservices are known as SAGA design patterns and are defined by the - {microprofile-lra-spec-url}[MicroProfile Long Running Actions specification]. -Unlike well known XA protocol, LRA is asynchronous and therefore much more scalable. Every LRA JAX-RS resource - (xref:participant.adoc[participant]) defines endpoints to be invoked when transaction needs to be - xref:participant.adoc#complete-participant-method[completed] or - xref:participant.adoc#compensate-participant-method[compensated]. - -LRA transactions need to be coordinated over REST API by the LRA coordinator. -xref:coordinator.adoc[Coordinator] -keeps track of all transactions and calls the @Compensate or @Complete endpoints for all participants involved in the particular -transaction. LRA transaction is first started, then joined by xref:participant.adoc[participant]. -Participant reports the successful finish of transaction by calling complete. Coordinator then calls the JAX-RS -xref:participant.adoc#complete-participant-method[complete] endpoint that was registered during the join of each -xref:participant.adoc[participant]. As the completed or compensated participants don't have to be on same instance, -the whole architecture is highly scalable. - -image::lra/lra-complete-lb.svg[Complete] - -In case of error during the LRA transaction, participant reports cancel of LRA to coordinator. -xref:coordinator.adoc[Coordinator] calls compensate on all the joined participants. - -image::lra/lra-compensate-lb-error.svg[Cancel] - -When participant joins the LRA with timeout defined - `@LRA(value = LRA.Type.REQUIRES_NEW, timeLimit = 5, timeUnit = ChronoUnit.MINUTES)`, coordinator compensate if - timeout occurs before close is reported by participants. - -image::lra/lra-compensate-lb-timeout.svg[Timeout] - -=== Example -The following example shows how a simple LRA participant starts and joins a transaction after calling the '/start-example' resource. -When startExample method finishes successfully, close is reported to xref:coordinator.adoc[coordinator] -and `/complete-example` endpoint is called by coordinator to confirm successful closure of the LRA. - -If an exception occurs during startExample method execution, coordinator receives cancel call and `/compensate-example` -is called by coordinator to compensate for cancelled LRA transaction. - -[source,java] -.Example of simple LRA participant ----- -@PUT -@LRA(LRA.Type.REQUIRES_NEW) <1> -@Path("start-example") -public Response startExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, <2> - String data) { - if (data.contains("BOOM")) { - throw new RuntimeException("BOOM 💥"); <3> - } - - LOGGER.info("Data " + data + " processed 🏭"); - return Response.ok().build(); <4> -} - -@PUT -@Complete <5> -@Path("complete-example") -public Response completeExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { - LOGGER.log(Level.INFO, "LRA id: {0} completed 🎉", lraId); - return LRAResponse.completed(); -} - -@PUT -@Compensate <6> -@Path("compensate-example") -public Response compensateExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { - LOGGER.log(Level.SEVERE, "LRA id: {0} compensated 🦺", lraId); - return LRAResponse.compensated(); -} ----- -<1> This JAX-RS PUT method will start new LRA transactions and join it before method body gets executed -<2> LRA id assigned by coordinator to this LRA transaction -<3> When method execution finishes exceptionally, cancel signal for this particular LRA is sent to coordinator -<4> When method execution finishes successfully, complete signal for this particular LRA is sent to coordinator -<5> Method which will be called by coordinator when LRA is completed -<6> Method which will be called by coordinator when LRA is canceled - -=== Configuration - -[source,yaml] -.Example of lra configuration ----- -mp.lra: - coordinator.url: http://localhost:8070/lra-coordinator <1> - propagation.active: true <2> - participant.url: http://coordinator.visibe.host:80/awsomeapp <3> ----- -<1> Url of coordinator -<2> Propagate LRA headers LRA_HTTP_CONTEXT_HEADER and LRA_HTTP_PARENT_CONTEXT_HEADER through non-LRA endpoints -<3> Url of the LRA enabled service overrides standard base uri, -so coordinator can call load-balancer instead of the service - -For more information continue to {microprofile-lra-spec-url}[MicroProfile Long Running Actions specification]. \ No newline at end of file diff --git a/docs/mp/lra/participant.adoc b/docs/mp/lra/participant.adoc deleted file mode 100644 index b69ec9eb2b7..00000000000 --- a/docs/mp/lra/participant.adoc +++ /dev/null @@ -1,272 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Participant -:toc: -:toc-placement: preamble -:description: Long Running Actions -:keywords: helidon, mp, lra -:feature-name: Long Running Actions -:spec-version: 1.0-RC3 -:javadoc-link: https://download.eclipse.org/microprofile/microprofile-lra-{spec-version}/apidocs/org/eclipse/microprofile/lra/annotation/ -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -== Participant - -The Participant, or Compensator, is an LRA resource with at least one of the JAX-RS(or non-JAX-RS) methods annotated with -link:{microprofile-lra-javadoc-url}Compensate.html[@Compensate] or link:{microprofile-lra-javadoc-url}AfterLRA.html[@AfterLRA]. - - -=== @LRA [[lra-method]] - -link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html[~javadoc~] - -Marks JAX-RS method which should run in LRA context and needs to be accompanied by at least minimal set of mandatory -participant methods(<>. -If the method responds with 500 or 202, coordinator will eventually try the call again. -If participant has <> xref:coordinator.adoc[coordinator] -retrieves the status to find out if retry should be done. - - -==== JAX-RS variant with supported LRA context values: - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA - -[source,java] ----- -@PUT -@Path("/compensate") -@Compensate -public Response compensateWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, - @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parent){ - return LRAResponse.compensated(); -} ----- - -==== Non JAX-RS variant with supported LRA context values: - -* URI with LRA id - -[source,java] ----- -@Compensate -public void compensate(URI lraId) ----- - -=== @Complete [[complete-participant-method]] - -{microprofile-lra-javadoc-url}Complete.html[~javadoc~] - -WARNING: Expected to be called by LRA xref:coordinator.adoc[coordinator] only! - -Complete method is called by xref:coordinator.adoc[coordinator] when LRA is successfully closed. -If the method responds with 500 or 202, coordinator will eventually try the call again. -If participant has <>, xref:coordinator.adoc[coordinator] retrieves the status to find out if retry should be done. - -==== JAX-RS variant with supported LRA context values: - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA - -[source,java] ----- -@PUT -@Path("/complete") -@Complete -public Response complete(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, - @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parentLraId) ----- - -==== Non JAX-RS variant with supported LRA context values: - -* URI with LRA id - -[source,java] ----- -@Complete -public void complete(URI lraId) ----- - -=== @Forget - -link:{microprofile-lra-javadoc-url}/Forget.html[~javadoc~] - -WARNING: Expected to be called by LRA xref:coordinator.adoc[coordinator] only! - -<> to xref:coordinator.adoc[coordinator]. -When xref:coordinator.adoc[coordinator] decides all the participants have finished, method annotated with @Forget is called. - -==== JAX-RS variant with supported LRA context values: - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA - -[source,java] ----- -@DELETE -@Path("/forget") -@Forget -public Response forget(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId, - @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parent) ----- - -==== Non JAX-RS variant with supported LRA context values: - -* URI with LRA id - -[source,java] ----- -@Forget -public void forget(URI lraId) -} ----- - -=== @Leave - -{microprofile-lra-javadoc-url}/ws/rs/Leave.html[~javadoc~] - -Method annotated with @Leave called with LRA context(with header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER]) informs xref:coordinator.adoc[coordinator] that current participant is leaving the LRA. -Method body is executed after leave signal is sent. -As a result, participant methods complete and compensate won't be called when the particular LRA ends. - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction - -[source,java] ----- -@PUT -@Path("/leave") -@Leave -public Response leaveLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraIdtoLeave) ----- - -=== @Status [[status-participant-method]] - -link:{microprofile-lra-javadoc-url}Status.html[~javadoc~] - -WARNING: Expected to be called by LRA xref:coordinator.adoc[coordinator] only! - -If the coordinator's call to the participant's method fails, then it will retry the call. -If the participant is not idempotent, then it may need to report its state to coordinator by declaring method -annotated with @Status for reporting if previous call did change participant status. -xref:coordinator.adoc[Coordinator] can call it and decide if compensate or complete retry is needed. - -==== JAX-RS variant with supported LRA context values: - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_CONTEXT_HEADER[LRA_HTTP_CONTEXT_HEADER] - id of the LRA transaction -* link:{microprofile-lra-javadoc-url}ParticipantStatus.html[ParticipantStatus] - Status of the participant reported to xref:coordinator.adoc[coordinator] - -[source,java] ----- -@GET -@Path("/status") -@Status -public Response reportStatus(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) { - return Response.status(ParticipantStatus.FailedToCompensate).build(); -} ----- - -==== Non JAX-RS variant with supported LRA context values: - -* URI with LRA id -* link:{microprofile-lra-javadoc-url}ParticipantStatus.html[ParticipantStatus] - Status of the participant reported to xref:coordinator.adoc[coordinator] - -[source,java] ----- -@Status -public Response reportStatus(URI lraId){ - return Response.ok(ParticipantStatus.FailedToCompensate).build(); -} ----- - -=== @AfterLRA [[after-participant-method]] - -link:{microprofile-lra-javadoc-url}AfterLRA.html[~javadoc~] - -WARNING: Expected to be called by LRA xref:coordinator.adoc[coordinator] only! - -Method annotated with link:{microprofile-lra-javadoc-url}AfterLRA.html[@AfterLRA] in the same class as the one with @LRA annotation gets invoked after particular LRA finishes. - -==== JAX-RS variant with supported LRA context values: - -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_ENDED_CONTEXT_HEADER[LRA_HTTP_ENDED_CONTEXT_HEADER] - id of the finished LRA transaction -* Header link:{microprofile-lra-javadoc-url}/ws/rs/LRA.html#LRA_HTTP_PARENT_CONTEXT_HEADER[LRA_HTTP_PARENT_CONTEXT_HEADER] - parent LRA id in case of nested LRA -* link:{microprofile-lra-javadoc-url}LRAStatus.html[LRAStatus] - Final status of the LRA (link:{microprofile-lra-javadoc-url}LRAStatus.html#Cancelled[Cancelled], link:{microprofile-lra-javadoc-url}LRAStatus.html#Closed[Closed], link:{microprofile-lra-javadoc-url}LRAStatus.html#FailedToCancel[FailedToCancel], {microprofile-lra-javadoc-url}LRAStatus.html#FailedToClose[FailedToClose]) - -[source,java] ----- -@PUT -@Path("/finished") -@AfterLRA -public Response whenLRAFinishes(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId, - @HeaderParam(LRA_HTTP_PARENT_CONTEXT_HEADER) URI parentLraId, - LRAStatus status) ----- - -==== Non JAX-RS variant with supported LRA context values: - -* URI with finished LRA id -* link:{microprofile-lra-javadoc-url}LRAStatus.html[LRAStatus] - Final status of the LRA (link:{microprofile-lra-javadoc-url}LRAStatus.html#Cancelled[Cancelled], link:{microprofile-lra-javadoc-url}LRAStatus.html#Closed[Closed], link:{microprofile-lra-javadoc-url}LRAStatus.html#FailedToCancel[FailedToCancel], {microprofile-lra-javadoc-url}LRAStatus.html#FailedToClose[FailedToClose]) - -[source,java] ----- -public void whenLRAFinishes(URI lraId, LRAStatus status) ----- - diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 6e864efec6d..6f60aae8bf0 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -402,14 +402,11 @@ backend: value: "verified_user" - type: "MENU" title: "Long Running Actions" - dir: "lra" glyph: type: "icon" value: "pending_actions" sources: - - "introduction.adoc" - - "coordinator.adoc" - - "participant.adoc" + - "lra.adoc" - type: "MENU" title: "Metrics" dir: "metrics" From e3d6e577c0aa87e221b34223029646464077bdd5 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Sun, 17 Jul 2022 19:58:03 -0500 Subject: [PATCH 33/51] Some fine-tuning of metrics and OpenAPI doc updates (#4563) --- .../metrics/metrics-capable-components.adoc | 31 ++++++++++++------- .../metrics/prometheus-exemplar-support.adoc | 7 ++--- docs/mp/metrics/metrics.adoc | 5 +++ docs/mp/openapi.adoc | 15 ++++----- docs/se/metrics/metrics.adoc | 15 ++++++--- 5 files changed, 44 insertions(+), 29 deletions(-) diff --git a/docs/includes/metrics/metrics-capable-components.adoc b/docs/includes/metrics/metrics-capable-components.adoc index e84f2332665..3e6874a59ac 100644 --- a/docs/includes/metrics/metrics-capable-components.adoc +++ b/docs/includes/metrics/metrics-capable-components.adoc @@ -39,27 +39,36 @@ This document explains Helidon {h1-prefix} metrics-capable components and applic // tag::all-beginning-text[] -Think of Helidon metrics in three related but different parts: +Think of Helidon metrics in several related but different parts: -* The Helidon metrics API. +=== APIs +* The Helidon metrics API + -The API allows your code to register, look-up, remove, and update metrics using +This API allows your code to register, look-up, remove, and update metrics using the `RegistryFactory`, `MetricRegistry`, and individual metrics interfaces. +* The Helidon metrics REST service API ++ +This API allows your code to set up and respond to the `/metrics` endpoint so clients can retreive metrics information. + +=== Implementations of the APIs * Implementations of the Helidon metrics API. + -Helidon provides two and selects which one to use at runtime, +Helidon provides two--minimal and full-featured--and selects which one to use at runtime, based on what components are present on the runtime path and whether metrics is configured to be enabled or disabled. -* The built-in Helidon metrics web service. + -This service supports the `/metrics` endpoint by which clients can retrieve metadata and -values of the registered metrics. -ifeval::["{h1-prefix}" == "MP"] -Helidon MP apps which use metrics enable the metrics service by default. +ifdef::mp-flavor[By default, Helidon MP services use the full-featured implementation.] +ifdef::se-flavor[You control which implementation your Helidon SE service uses by which dependency you add to your project.] +* Implementations of the Helidon metrics REST service API. ++ +Helidon provides two--minimal and full-featured--and selects which one to use at runtime. ++ +ifdef::mp-flavor[] +By default, Helidon MP apps which use metrics use the full-featured metrics REST service by default. endif::[] -ifeval::["{h1-prefix}" == "SE"] +ifdef::se-flavor[] Your Helidon SE app provides this feature (if at all) by explicitly using the `MetricsSupport` interface. + -Most Helidon applications are web-based and their developers choose to expose the built-in metrics web service. +Most Helidon SE applications are web-based and their developers choose to expose the built-in metrics web service. But by separating the parts of metrics this way, Helidon allows non-web apps to work with metrics as well, just without the web service support. endif::[] diff --git a/docs/includes/metrics/prometheus-exemplar-support.adoc b/docs/includes/metrics/prometheus-exemplar-support.adoc index 8b2c3cb2c87..4209f899d4f 100644 --- a/docs/includes/metrics/prometheus-exemplar-support.adoc +++ b/docs/includes/metrics/prometheus-exemplar-support.adoc @@ -60,14 +60,13 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -Also, include the Helidon integration module for a tracing implementation, see +Also, include the Helidon integration module for a tracing implementation (such as ifdef::se-flavor[] -xref:{rootdir}/se/tracing.adoc#zipkin-tracing[Helidon Zipkin] +xref:{rootdir}/se/tracing.adoc#zipkin-tracing[Helidon Zipkin]) endif::[] ifdef::mp-flavor[] -xref:{rootdir}/mp/tracing.adoc#zipkin-tracing[Helidon Zipkin]: +xref:{rootdir}/mp/tracing.adoc#zipkin-tracing[Helidon Zipkin]) endif::[] -support: include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-dependency] Add the Helidon tracing component itself: diff --git a/docs/mp/metrics/metrics.adoc b/docs/mp/metrics/metrics.adoc index c88aa00edb2..b37aca89df2 100644 --- a/docs/mp/metrics/metrics.adoc +++ b/docs/mp/metrics/metrics.adoc @@ -52,6 +52,11 @@ include::{rootdir}/includes/dependencies.adoc[] ---- +Adding this dependency packages the full-featured metrics implementation with your service. + +=== Other packaging options +Helidon gives you flexibility in how you make metrics available to your service. xref:{rootdir}/mp/metrics/metrics-capable-components.adoc[This document] explains your options. + == Usage include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-body] diff --git a/docs/mp/openapi.adoc b/docs/mp/openapi.adoc index 9b992c251f5..4e20e0d0faf 100644 --- a/docs/mp/openapi.adoc +++ b/docs/mp/openapi.adoc @@ -69,17 +69,11 @@ document shows what changes you need to make to your application and how to acce the OpenAPI document for your application at runtime. === Changing your application -Helidon MP conforms to the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI specification], -which was inspired by the link:https://github.com/OAI/OpenAPI-Specification[OpenAPI spec] itself. -Helidon MP includes a link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[complete OpenAPI example] -based on the MP quick-start sample app. - -To use OpenAPI from your Helidon MP app: +To use OpenAPI from your Helidon MP app, in addition to adding dependencies as described above: -1. Edit your `pom.xml`. -2. Furnish OpenAPI information about your application's endpoints. -3. Update your application's configuration (optional). +1. Furnish OpenAPI information about your application's endpoints. +2. Update your application's configuration (optional). include::{rootdir}/includes/openapi.adoc[tag=furnish-openapi-info] @@ -103,6 +97,9 @@ include::{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[ == Examples +Helidon MP includes a link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[complete OpenAPI example] +based on the MP quick-start sample app. The rest of this section shows, step-by-step, how one might change the original QuickStart service to adopt OpenAPI. + === Helidon MP Basic OpenAPI Example This example shows a simple greeting application, similar to the one from the diff --git a/docs/se/metrics/metrics.adoc b/docs/se/metrics/metrics.adoc index 77edc04a909..3e1221a8a15 100644 --- a/docs/se/metrics/metrics.adoc +++ b/docs/se/metrics/metrics.adoc @@ -36,21 +36,26 @@ include::{rootdir}/includes/se.adoc[] == Overview Helidon SE metrics is inspired by--but does not fully implement--the MicroProfile Metrics specification. -In particular, Helidon metrics furnishes +In particular, the Helidon metrics subsystem furnishes include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=overview] - - +// Maven coordinates comes next include::{rootdir}/includes/dependencies.adoc[] +.Packaging full-featured metrics [source,xml] ---- - io.helidon.metrics.api + io.helidon.metrics helidon-metrics ---- +Adding this dependency packages the full-featured metrics implementation with your service. + +=== Other packaging options +Helidon gives you flexibility in how you make metrics available to your service. xref:{rootdir}/se/metrics/metrics-capable-components.adoc[This document] explains your options. + == Usage include::{rootdir}/includes/metrics/metrics-shared.adoc[tag=usage-body] @@ -177,7 +182,7 @@ To use it, your service registers Prometheus support with your routing set-up. You can customize its configuration. For information about using Prometheus, see the Prometheus documentation: https://prometheus.io/docs/introduction/overview/. -NOTE: Helidon's fully-functional, built-in metrics implementation supports Prometheus (OpenMetrics) output. Use this optional support only if you want to use the Prometheus API from your application code. +NOTE: Helidon's fully-functional, built-in metrics implementation supports Prometheus (OpenMetrics) output. Use the optional support described in _this_ section only if you want to use the Prometheus _API_ from your application code. [#prom-maven-coordinates] ==== Maven Coordinates From 27b991a3f8e63eab8bc28a959b887b8f9c6da9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Mon, 18 Jul 2022 15:58:23 +0200 Subject: [PATCH 34/51] Security Providers Documentation (#4557) Security Providers Documentation Signed-off-by: David Kral --- docs/config/config_reference.adoc | 10 ++ ...urity_providers_common_EvictableCache.adoc | 59 ++++++++ ...ders_google_login_GoogleTokenProvider.adoc | 70 +++++++++ ...ty_providers_header_HeaderAtnProvider.adoc | 71 ++++++++++ ...viders_httpauth_HttpBasicAuthProvider.adoc | 1 + ...iders_httpauth_HttpDigestAuthProvider.adoc | 75 ++++++++++ ...y_providers_httpsign_HttpSignProvider.adoc | 134 ++++++++++++++++++ ...ders_httpsign_InboundClientDefinition.adoc | 61 ++++++++ ...ign_SignedHeadersConfig_HeadersConfig.adoc | 51 +++++++ ...dcs_mapper_IdcsMtRoleMapperRxProvider.adoc | 76 ++++++++++ ..._idcs_mapper_IdcsRoleMapperRxProvider.adoc | 72 ++++++++++ ...on_security_providers_jwt_JwtProvider.adoc | 91 ++++++++++++ ..._security_providers_oidc_OidcProvider.adoc | 1 + ...rity_providers_oidc_common_OidcConfig.adoc | 1 + docs/includes/attributes.adoc | 8 ++ docs/includes/security/providers/abac.adoc | 13 +- .../security/providers/google-login.adoc | 39 +---- .../security/providers/header-assertion.adoc | 42 +----- .../security/providers/http-basic-auth.adoc | 43 +----- .../security/providers/http-digest-auth.adoc | 34 +---- .../security/providers/http-signatures.adoc | 54 +------ .../security/providers/idcs-role-mapper.adoc | 43 +----- docs/includes/security/providers/jwt.adoc | 51 +------ docs/includes/security/providers/oidc.adoc | 78 +--------- docs/se/security/providers.adoc | 19 +++ .../security/providers/abac/AbacProvider.java | 9 +- .../providers/abac/AbacProviderService.java | 5 +- .../providers/common/EvictableCache.java | 15 +- security/providers/google-login/pom.xml | 12 ++ .../google/login/GoogleTokenProvider.java | 18 ++- .../google/login/GoogleTokenService.java | 7 +- .../src/main/java/module-info.java | 2 + security/providers/header/pom.xml | 12 ++ .../providers/header/HeaderAtnProvider.java | 15 +- .../providers/header/HeaderAtnService.java | 7 +- .../header/src/main/java/module-info.java | 4 +- .../httpauth/HttpBasicAuthProvider.java | 3 +- .../httpauth/HttpDigestAuthProvider.java | 28 +++- .../httpauth/HttpDigestAuthService.java | 6 +- .../http-auth/src/main/java/module-info.java | 3 +- security/providers/http-sign/pom.xml | 12 ++ .../providers/httpsign/HttpSignProvider.java | 17 ++- .../providers/httpsign/HttpSignService.java | 7 +- .../httpsign/InboundClientDefinition.java | 11 +- .../httpsign/SignedHeadersConfig.java | 13 +- .../http-sign/src/main/java/module-info.java | 4 +- security/providers/idcs-mapper/pom.xml | 12 ++ .../mapper/IdcsMtRoleMapperRxProvider.java | 12 +- .../mapper/IdcsRoleMapperProviderService.java | 7 +- .../idcs/mapper/IdcsRoleMapperRxProvider.java | 8 +- .../mapper/IdcsRoleMapperRxProviderBase.java | 8 +- .../src/main/java/module-info.java | 4 +- security/providers/jwt/pom.xml | 12 ++ .../security/providers/jwt/JwtProvider.java | 21 ++- .../providers/jwt/JwtProviderService.java | 7 +- .../jwt/src/main/java/module-info.java | 4 +- .../providers/oidc/common/OidcConfig.java | 1 + 57 files changed, 1102 insertions(+), 401 deletions(-) create mode 100644 docs/config/io_helidon_security_providers_common_EvictableCache.adoc create mode 100644 docs/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc create mode 100644 docs/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc create mode 100644 docs/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc create mode 100644 docs/config/io_helidon_security_providers_jwt_JwtProvider.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index 4b862222931..6adca3245e1 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -32,11 +32,21 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser (security.providers.httpauth.ConfigUserStore)] - xref:{rootdir}/config/io_helidon_webserver_cors_CrossOriginConfig.adoc[CrossOriginConfig (webserver.cors)] - xref:{rootdir}/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc[DelayingRetryPolicy (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_security_providers_common_EvictableCache.adoc[EvictableCache (security.providers.common)] +- xref:{rootdir}/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc[GoogleTokenProvider (security.providers.google.login)] +- 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)] - xref:{rootdir}/config/io_helidon_health_HealthSupport.adoc[HealthSupport (health)] - xref:{rootdir}/config/io_helidon_servicecommon_rest_HelidonRestServiceSupport.adoc[HelidonRestServiceSupport (servicecommon.rest)] - xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[HttpBasicAuthProvider (security.providers.httpauth)] +- xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc[HttpDigestAuthProvider (security.providers.httpauth)] +- xref:{rootdir}/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc[HttpSignProvider (security.providers.httpsign)] +- xref:{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc[IdcsMtRoleMapperRxProvider (security.providers.idcs.mapper)] +- xref:{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc[IdcsRoleMapperRxProvider (security.providers.idcs.mapper)] +- xref:{rootdir}/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc[InboundClientDefinition (security.providers.httpsign)] - xref:{rootdir}/config/io_helidon_tracing_jaeger_JaegerTracerBuilder.adoc[JaegerTracer (tracing.jaeger)] - xref:{rootdir}/config/io_helidon_faulttolerance_Retry_JitterRetryPolicy.adoc[JitterRetryPolicy (faulttolerance.Retry)] +- xref:{rootdir}/config/io_helidon_security_providers_jwt_JwtProvider.adoc[JwtProvider (security.providers.jwt)] - xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig (common.pki)] - xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsSettings.adoc[KeyPerformanceIndicatorMetricsSettings (metrics.api)] - xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_KeystoreBuilder.adoc[KeystoreBuilder (common.pki.KeyConfig)] diff --git a/docs/config/io_helidon_security_providers_common_EvictableCache.adoc b/docs/config/io_helidon_security_providers_common_EvictableCache.adoc new file mode 100644 index 00000000000..f99c731e3ed --- /dev/null +++ b/docs/config/io_helidon_security_providers_common_EvictableCache.adoc @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.common.EvictableCache +:keywords: helidon, config, io.helidon.security.providers.common.EvictableCache +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.common.EvictableCache +include::{rootdir}/includes/attributes.adoc[] + += EvictableCache (security.providers.common) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io/helidon/security/providers/common/EvictableCache.html[io.helidon.security.providers.common.EvictableCache] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`cache-enabled` |boolean |`true` |If the cacheEnabled is set to false, no caching will be done. + Otherwise (default behavior) evictable caching will be used. +|`cache-evict-delay-millis` |long |`60000` |Delay from the creation of the cache to first eviction +|`cache-evict-period-millis` |long |`300000` |How often to evict records +|`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 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. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc b/docs/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc new file mode 100644 index 00000000000..d22b783995c --- /dev/null +++ b/docs/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.google.login.GoogleTokenProvider +:keywords: helidon, config, io.helidon.security.providers.google.login.GoogleTokenProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.google.login.GoogleTokenProvider +include::{rootdir}/includes/attributes.adoc[] + += GoogleTokenProvider (security.providers.google.login) Configuration + +// tag::config[] + +Google Authentication provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.google.login/io/helidon/security/providers/google/login/GoogleTokenProvider.html[io.helidon.security.providers.google.login.GoogleTokenProvider] + + +[source,text] +.Config key +---- +google-login +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|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 + 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. +|`proxy-host` |string |{nbsp} |Set proxy host when talking to Google. +|`proxy-port` |int |`80` |Set proxy port when talking to Google. +|`realm` |string |`helidon` |Set the authentication realm to build challenge, defaults to "helidon". +|`token` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |``Authorization` header with `bearer` prefix` |Token provider to extract Google access token from request, defaults to "Authorization" header with a "bearer " prefix. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc b/docs/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc new file mode 100644 index 00000000000..10768cdf4b5 --- /dev/null +++ b/docs/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.header.HeaderAtnProvider +:keywords: helidon, config, io.helidon.security.providers.header.HeaderAtnProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.header.HeaderAtnProvider +include::{rootdir}/includes/attributes.adoc[] + += HeaderAtnProvider (security.providers.header) Configuration + +// tag::config[] + +Security provider that extracts a username (or service name) from a header. + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.header/io/helidon/security/providers/header/HeaderAtnProvider.html[io.helidon.security.providers.header.HeaderAtnProvider] + + +[source,text] +.Config key +---- +header-atn +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`atn-token` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |Token handler to extract username from request. +|`authenticate` |boolean |`true` |Whether to authenticate requests. +|`optional` |boolean |`false` |Whether authentication is required. + By default, request will fail if the username cannot be extracted. + 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. +|`principal-type` |SubjectType (USER, SERVICE) |`USER` |Principal type this provider extracts (and also propagates). +|`propagate` |boolean |`false` |Whether to propagate identity. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc index bc28b134477..a45406df1d0 100644 --- a/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc +++ b/docs/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc @@ -58,6 +58,7 @@ Optional configuration options: |`optional` |boolean |`false` |Whether authentication is required. By default, request will fail if the authentication cannot be verified. 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} |Add a new outbound target to configure identity propagation or explicit username/password. |`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. diff --git a/docs/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc b/docs/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc new file mode 100644 index 00000000000..c085ec2504f --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpauth.HttpDigestAuthProvider +:keywords: helidon, config, io.helidon.security.providers.httpauth.HttpDigestAuthProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpauth.HttpDigestAuthProvider +include::{rootdir}/includes/attributes.adoc[] + += HttpDigestAuthProvider (security.providers.httpauth) Configuration + +// tag::config[] + +Http digest authentication security provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpauth/io/helidon/security/providers/httpauth/HttpDigestAuthProvider.html[io.helidon.security.providers.httpauth.HttpDigestAuthProvider] + + +[source,text] +.Config key +---- +http-digest-auth +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`algorithm` |Algorithm (MD5) |`MD5` |Digest algorithm to use. +|`nonce-timeout-millis` |long |`86400000` |How long will the nonce value be valid. When timed-out, browser will re-request username/password. +|`optional` |boolean |`false` |Whether authentication is required. + By default, request will fail if the authentication cannot be verified. + If set to false, request will process and this provider will abstain. +|`principal-type` |SubjectType (USER, SERVICE) |`USER` |Principal type this provider extracts (and also propagates). +|`qop` |Qop (NONE, AUTH) |`NONE` |Only `AUTH` supported. If left empty, uses the legacy approach (older RFC version). `AUTH-INT` is not supported. +|`realm` |string |`Helidon` |Set the realm to use when challenging users. +|`server-secret` |string |{nbsp} |The nonce is encrypted using this secret - to make sure the nonce we get back was generated by us and to + make sure we can safely time-out nonce values. + This secret must be the same for all service instances (or all services that want to share the same authentication). + Defaults to a random password - e.g. if deployed to multiple servers, the authentication WILL NOT WORK. You MUST + provide your own password to work in a distributed environment with non-sticky load balancing. +|`users` |xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser[]] |{nbsp} |Set user store to obtain passwords and roles based on logins. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc b/docs/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc new file mode 100644 index 00000000000..d336cd44870 --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpsign.HttpSignProvider +:keywords: helidon, config, io.helidon.security.providers.httpsign.HttpSignProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpsign.HttpSignProvider +include::{rootdir}/includes/attributes.adoc[] + += HttpSignProvider (security.providers.httpsign) Configuration + +// tag::config[] + +HTTP header signature provider. + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpsign/io/helidon/security/providers/httpsign/HttpSignProvider.html[io.helidon.security.providers.httpsign.HttpSignProvider] + + +[source,text] +.Config key +---- +http-signatures +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`backward-compatible-eol` |boolean |`false` |Enable support for Helidon versions before 3.0.0 (exclusive). + + Until version 3.0.0 (exclusive) there was a trailing end of line added to the signed + data. + To be able to communicate cross versions, we must configure this when talking to older versions of Helidon. + Default value is `false`. In Helidon 2.x, this switch exists as well and the default is `true`, to + allow communication between versions as needed. +|`headers` |HttpSignHeader[] (SIGNATURE, AUTHORIZATION, CUSTOM) |{nbsp} |Add a header that is validated on inbound requests. Provider may support more than + one header to validate. +|`inbound.keys` |xref:{rootdir}/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc[InboundClientDefinition[]] |{nbsp} |Add inbound configuration. This is used to validate signature and authenticate the + party. + + The same can be done through configuration: +

+ {
+  name = "http-signatures"
+  class = "HttpSignProvider"
+  http-signatures {
+      inbound {
+          # This configures the InboundClientDefinition
+          keys: [
+          {
+              key-id = "service1"
+              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 + 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 + the request. + + The same can be done through configuration: +
+ {
+  name = "http-signatures"
+  class = "HttpSignProvider"
+  http-signatures {
+      targets: [
+      {
+          name = "service2"
+          hosts = ["localhost"]
+          paths = ["/service2/.*"]
+
+          # This configures the OutboundTargetDefinition
+          signature {
+              key-id = "service1"
+              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. +|`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 +- put, post: same as above, with addition of: content-length, content-type and digest if present + +- for other methods: date, (request-target) + +Note that this provider DOES NOT validate the "Digest" HTTP header, only the signature. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc b/docs/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc new file mode 100644 index 00000000000..fc12cf5ffdb --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpsign.InboundClientDefinition +:keywords: helidon, config, io.helidon.security.providers.httpsign.InboundClientDefinition +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpsign.InboundClientDefinition +include::{rootdir}/includes/attributes.adoc[] + += InboundClientDefinition (security.providers.httpsign) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpsign/io/helidon/security/providers/httpsign/InboundClientDefinition.html[io.helidon.security.providers.httpsign.InboundClientDefinition] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`algorithm` |string |{nbsp} |Algorithm of signature used by this client. + Currently supported: + +- rsa-sha256 - asymmetric based on public/private keys +- 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. +|`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). +|`public-key` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig] |{nbsp} |For algorithms based on public/private key (such as rsa-sha256), this provides access to the public key of the client. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc b/docs/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc new file mode 100644 index 00000000000..9014b92a600 --- /dev/null +++ b/docs/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig +:keywords: helidon, config, io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig +include::{rootdir}/includes/attributes.adoc[] + += HeadersConfig (security.providers.httpsign.SignedHeadersConfig) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.httpsign.SignedHeadersConfig/io/helidon/security/providers/httpsign/SignedHeadersConfig/HeadersConfig.html[io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`always` |string[] |{nbsp} |Headers that must be signed (and signature validation or creation should fail if not signed or present) +|`if-present` |string[] |{nbsp} |Headers that must be signed if present in request. +|`method` |string |{nbsp} |HTTP method this header configuration is bound to. If not present, it is considered default header configuration. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc b/docs/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc new file mode 100644 index 00000000000..faf4efca29f --- /dev/null +++ b/docs/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperRxProvider +:keywords: helidon, config, io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperRxProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperRxProvider +include::{rootdir}/includes/attributes.adoc[] + += IdcsMtRoleMapperRxProvider (security.providers.idcs.mapper) Configuration + +// tag::config[] + +Multitenant IDCS role mapping provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.idcs.mapper/io/helidon/security/providers/idcs/mapper/IdcsMtRoleMapperRxProvider.html[io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperRxProvider] + + +[source,text] +.Config key +---- +idcs-role-mapper +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.SubjectMappingProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`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. +|`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 `IdcsMtRoleMapperRxProvider#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 `IdcsMtRoleMapperRxProvider#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 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 need to be added. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc b/docs/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc new file mode 100644 index 00000000000..9abecf80f54 --- /dev/null +++ b/docs/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.idcs.mapper.IdcsRoleMapperRxProvider +:keywords: helidon, config, io.helidon.security.providers.idcs.mapper.IdcsRoleMapperRxProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.idcs.mapper.IdcsRoleMapperRxProvider +include::{rootdir}/includes/attributes.adoc[] + += IdcsRoleMapperRxProvider (security.providers.idcs.mapper) Configuration + +// tag::config[] + +IDCS role mapping provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.idcs.mapper/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProvider.html[io.helidon.security.providers.idcs.mapper.IdcsRoleMapperRxProvider] + + +[source,text] +.Config key +---- +idcs-role-mapper +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.SubjectMappingProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`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. +|`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 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 need to be added. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_jwt_JwtProvider.adoc b/docs/config/io_helidon_security_providers_jwt_JwtProvider.adoc new file mode 100644 index 00000000000..a7e63d3bfdc --- /dev/null +++ b/docs/config/io_helidon_security_providers_jwt_JwtProvider.adoc @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.security.providers.jwt.JwtProvider +:keywords: helidon, config, io.helidon.security.providers.jwt.JwtProvider +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.security.providers.jwt.JwtProvider +include::{rootdir}/includes/attributes.adoc[] + += JwtProvider (security.providers.jwt) Configuration + +// tag::config[] + +JWT authentication provider + + +Type: link:{javadoc-base-url}/io.helidon.security.providers.jwt/io/helidon/security/providers/jwt/JwtProvider.html[io.helidon.security.providers.jwt.JwtProvider] + + +[source,text] +.Config key +---- +jwt +---- + + +This type provides the following service implementations: + +- `io.helidon.security.spi.SecurityProvider` +- `io.helidon.security.spi.AuthenticationProvider` + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`allow-impersonation` |boolean |`false` |Whether to allow impersonation by explicitly overriding + username from outbound requests using #EP_PROPERTY_OUTBOUND_USER property. + By default this is not allowed and identity can only be propagated. +|`allow-unsigned` |boolean |`false` |Configure support for unsigned JWT. + If this is set to `true` any JWT that has algorithm + set to `none` and no `kid` defined will be accepted. + Note that this has serious security impact - if JWT can be sent + from a third party, this allows the third party to send ANY JWT + and it would be accpted as valid. +|`atn-token.handler` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |Token handler to extract username from request. +|`atn-token.jwk.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |JWK resource used to verify JWTs created by other parties. +|`atn-token.jwt-audience` |string |{nbsp} |Audience expected in inbound JWTs. +|`atn-token.verify-signature` |boolean |`true` |Configure whether to verify signatures. + Signatures verification is enabled by default. You can configure the provider + not to verify signatures. + + Make sure your service is properly secured on network level and only + accessible from a secure endpoint that provides the JWTs when signature verification + is disabled. If signature verification is disabled, this service will accept ANY JWT +|`authenticate` |boolean |`true` |Whether to authenticate requests. +|`optional` |boolean |`false` |Whether authentication is required. + By default, request will fail if the username cannot be extracted. + If set to false, request will process and this provider will abstain. +|`principal-type` |SubjectType (USER, SERVICE) |`USER` |Principal type this provider extracts (and also propagates). +|`propagate` |boolean |`true` |Whether to propagate identity. +|`sign-token` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig] |{nbsp} |Configuration of outbound rules. +|`sign-token.jwk.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |JWK resource used to sign JWTs created by us. +|`sign-token.jwt-issuer` |string |{nbsp} |Issuer used to create new JWTs. +|`use-jwt-groups` |boolean |`true` |Claim `groups` from JWT will be used to automatically add + groups to current subject (may be used with jakarta.annotation.security.RolesAllowed annotation). + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc index 76ec6b5671f..6118af0fa58 100644 --- a/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc +++ b/docs/config/io_helidon_security_providers_oidc_OidcProvider.adoc @@ -85,6 +85,7 @@ Optional configuration options: Defaults to false. |`cookie-use` |boolean |`true` |Whether to use cookie to store JWT between requests. Defaults to `DEFAULT_COOKIE_USE`. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_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`. |`frontend-uri` |string |{nbsp} |Full URI of this application that is visible from user browser. diff --git a/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc index 537cd3b88a4..e98907d8ce6 100644 --- a/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc +++ b/docs/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc @@ -74,6 +74,7 @@ Optional configuration options: Defaults to false. |`cookie-use` |boolean |`true` |Whether to use cookie to store JWT between requests. Defaults to `DEFAULT_COOKIE_USE`. +|`cors` |xref:{rootdir}/config/io_helidon_webserver_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`. |`frontend-uri` |string |{nbsp} |Full URI of this application that is visible from user browser. diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 97b2891295e..5567426d5a8 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -202,6 +202,14 @@ endif::[] :webserver-staticcontent-javadoc-base-url: {webserver-javadoc-base-url}.staticcontent :webserver-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.cors :graphql-javadoc-base-url: {javadoc-base-url}/io.helidon.graphql.server +:security-provider-oidc-base-url: {javadoc-base-url}/io.helidon.security.providers.oidc +:security-provider-httpauth-base-url: {javadoc-base-url}/io.helidon.security.providers.httpauth +:security-provider-header-base-url: {javadoc-base-url}/io.helidon.security.providers.header +:security-provider-httpsign-base-url: {javadoc-base-url}/io.helidon.security.providers.httpsign +:security-provider-idcs-mapper-base-url: {javadoc-base-url}/io.helidon.security.providers.idcs.mapper +:security-provider-abac-base-url: {javadoc-base-url}/io.helidon.security.providers.abac +:security-provider-google-login-base-url: {javadoc-base-url}/io.helidon.security.providers.google.login +:security-provider-jwt-base-url: {javadoc-base-url}/io.helidon.security.providers.jwt // 3rd party versioned URLs :jaeger-doc-base-url: https://www.jaegertracing.io/docs/{version-lib-jaeger} diff --git a/docs/includes/security/providers/abac.adoc b/docs/includes/security/providers/abac.adoc index 95fb8ceb213..42e43f7d643 100644 --- a/docs/includes/security/providers/abac.adoc +++ b/docs/includes/security/providers/abac.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === ABAC Provider :description: Helidon Security ABAC Provider :keywords: helidon, security, authorization, abac +:feature-name: ABAC Security Provider Attribute based access control authorization provider. @@ -35,17 +36,9 @@ Attribute based access control authorization provider. ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.abac.AbacProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -abac ----- +include::{rootdir}/config/io_helidon_security_providers_abac_AbacProvider.adoc[leveloffset=+2,tag=config] ==== Example code diff --git a/docs/includes/security/providers/google-login.adoc b/docs/includes/security/providers/google-login.adoc index 5279de1a75b..0e467b49959 100644 --- a/docs/includes/security/providers/google-login.adoc +++ b/docs/includes/security/providers/google-login.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === Google Login Provider :description: Helidon Security Google Login Provider :keywords: helidon, security, google +:feature-name: Google Login Security Provider Authenticates a token from request against Google identity provider @@ -35,17 +36,9 @@ Authenticates a token from request against Google identity provider ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.google.login.GoogleTokenProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -google-login ----- +include::{rootdir}/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -60,32 +53,6 @@ security: client-id: "Google client id" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`client-id` |{nbsp} |Client id of an application. To create an application, use - the Google developer console (https://developers.google.com/identity/sign-in/web/sign-in) -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is - an important distinction when more than one provider is used -|`realm` |`helidon` |Realm used in the challenge when authentication is not provided and it is required -|`proxy-host` |none |Configuration of a proxy host to use when authenticating the user -|`proxy-port` |`80` |Proxy port -|`token` |`Authorization` header with `bearer` prefix |Configuration of the location of the token (see `TokenHandler`) -|`outbound` |{nbsp} |A list of outbound configurations -|`outbound.*.name` |{nbsp} |Required name of outbound configuration -|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated -|`outbound.*.password` |{nbsp} |Optional password used for outbound security -|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for -|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression -|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression -|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for -|=== - ==== How does it work? We expect to receive a token (with sufficient scopes) from the inbound request, such as when using the Google login button on a page. diff --git a/docs/includes/security/providers/header-assertion.adoc b/docs/includes/security/providers/header-assertion.adoc index 5ed22967edd..514e9c67a6e 100644 --- a/docs/includes/security/providers/header-assertion.adoc +++ b/docs/includes/security/providers/header-assertion.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === Header Authentication Provider :description: Helidon Security Header Provider :keywords: helidon, security, header +:feature-name: Header Authentication Security Provider Asserts user or service identity based on a value of a header. @@ -35,17 +36,9 @@ Asserts user or service identity based on a value of a header. ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.header.HeaderAtnProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -header-atn ----- +include::{rootdir}/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -69,35 +62,6 @@ security: header: "X-Service-Auth" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is - an important distinction when more than one provider is used -|`authenticate` |`true` |If set to `false`, authentication will not be attempted (outbound security can still be used) -|`propagate` |`false` |If explicitly set to `false`, identity propagation will not be done. Otherwise it is done if an `outbound` - section is configured -|`principal-type` |`USER` |Can be `USER` or `SERVICE` -|`atn-token` |`none` | Token extraction and propagation, you can define which header to use and how to extract it -|`outbound` |{nbsp} |A list of outbound configurations -|`outbound.*.name` |{nbsp} |Required name of outbound configuration -|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated -|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for -|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression -|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression -|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for -|`outbound.*.outbound-token` |same as `atn-token` |Configuration of outbound header used to propagate -|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token -|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"username "` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`) -|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"username %1s"` -|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"username (.*)"` -|=== - ==== How does it work? This provider inspects a specified request header and extracts the username/service name from it and asserts it as current subject's principal. diff --git a/docs/includes/security/providers/http-basic-auth.adoc b/docs/includes/security/providers/http-basic-auth.adoc index 05cb4cb2a43..99c2b938da1 100644 --- a/docs/includes/security/providers/http-basic-auth.adoc +++ b/docs/includes/security/providers/http-basic-auth.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === HTTP Basic Authentication Provider :description: Helidon Security HTTP Basic Provider :keywords: helidon, security, basic +:feature-name: HTTP Basic Authentication Security Provider HTTP Basic authentication support @@ -35,17 +36,9 @@ HTTP Basic authentication support ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.httpauth.HttpBasicAuthProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -http-basic-auth ----- +include::{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -78,36 +71,6 @@ security: password: "${CLEAR=password}" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is -an important distinction when more than one provider is used -|`realm` |`helidon` |The realm shown in challenge when user accesses a service without authentication -|`principal-type` |`USER` |Type of authenticated entity - either `USER` or `SERVICE`, can be used in combination with - other authentication mechanism to authenticate both the user (as in person sitting in front of a computer) - and a service (as in the application requesting this service on user's behalf) -|`users` |{nbsp} |List of users when using configuration based approach. As an alternative, you can implement a java service (see below). -|`outbound` |{nbsp} |A list of outbound configurations -|`outbound.*.name` |{nbsp} |Required name of outbound configuration -|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated -|`outbound.*.password` |{nbsp} |Optional password used for outbound security -|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for -|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression -|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression -|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for -|`outbound.*.outbound-token` |`Authorization` header with `basic` prefix |Configuration of outbound header used to propagate -|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token -|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"basic "` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`) -|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"basic %1s"` -|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"basic (.*)"` -|=== - ==== How does it work? See https://tools.ietf.org/html/rfc7617[]. diff --git a/docs/includes/security/providers/http-digest-auth.adoc b/docs/includes/security/providers/http-digest-auth.adoc index c2c16708963..88db728295c 100644 --- a/docs/includes/security/providers/http-digest-auth.adoc +++ b/docs/includes/security/providers/http-digest-auth.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === HTTP Digest Authentication Provider :description: Helidon Security HTTP Digest Provider :keywords: helidon, security, digest +:feature-name: HTTP Digest Authentication Security Provider HTTP Digest authentication support @@ -35,17 +36,9 @@ HTTP Digest authentication support ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.httpauth.HttpDigestAuthProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -http-digest-auth ----- +include::{rootdir}/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -66,27 +59,6 @@ security: roles: ["user", "admin"] ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is -an important distinction when more than one provider is used -|`realm` |`helidon` |The realm shown in challenge when user accesses a service without authentication -|`principal-type` |`USER` |Type of authenticated entity - either `USER` or `SERVICE`, can be used in combination with - other authentication mechanism to authenticate both the user (as in person sitting in front of a computer) - and a service (as in the application requesting this service on user's behalf) -|`users` |{nbsp} |List of users when using configuration based approach. As an alternative, you can implement a java service (see below). -|`algorithm` |`MD5` |Only `MD5` supported -|`nonce-timeout-millis` |1 day |Number of milliseconds for the nonce timeout -|`server-secret` |random |A string to use as a server secret - this is to use digest auth between multiple servers (e.g. when in a cluster). Used to encrypt nonce. This must not be known outside of this app, as others may create digest requests we would trust. -|`qop` |`NONE` |only `AUTH` supported. If left empty, uses the legacy approach (older RFC version). `AUTH-INT` is not supported. -|=== - ==== How does it work? See https://tools.ietf.org/html/rfc7616[]. diff --git a/docs/includes/security/providers/http-signatures.adoc b/docs/includes/security/providers/http-signatures.adoc index 84118a4acac..414092347e2 100644 --- a/docs/includes/security/providers/http-signatures.adoc +++ b/docs/includes/security/providers/http-signatures.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === HTTP Signatures Provider :description: Helidon Security HTTP Signatures Provider :keywords: helidon, security +:feature-name: HTTP Signatures Security Provider Support for HTTP Signatures. @@ -35,17 +36,10 @@ Support for HTTP Signatures. ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.httpsign.HttpSignProvider ----- +==== Overview + +include::{rootdir}/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc[leveloffset=+2,tag=config] -[source,text] -.Provider configuration key ----- -http-signatures ----- ==== Example code @@ -88,46 +82,6 @@ security: key.alias: "myPrivateKey" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is - an important distinction when more than one provider is used -|`realm` | `helidon` |Realm used for challenge when request does not have a signature -|`headers` | `[SIGNATURE,AUTHORIZATION]` |Headers to look for inbound signatures and to store outbound signatures -|`sign-headers` | `always = ["date"]` |Headers to be signed -|`sign-headers.*.method` |default for all methods |Method this configuration is valid for -|`sign-headers.*.always` | {nbsp} |Array of headers to be always required in the request signature -|`sign-headers.*.if-present` |{nbsp} |Array of headers to be part of the signatures if present in the request -|`inbound` |{nbsp} |Configuration of inbound traffic for authenticating incoming requests -|`inbound.keys` |{nbsp} |Configuration of signature keys to verify incoming requests -|`inbound.keys.*.key-id` |{nbsp} |Key id as used in inbound signature to find the correct certificate/hmac configuration to verify the signature -|`inbound.keys.*.principal-name` |{nbsp} |The principal name (or user name) asserted when the signature is valid -|`inbound.keys.*.principal-type` |`SERVICE` |The type of principal to assert (can be `USER`) -|`inbound.keys.*.algorithm` |according to other configuration |`hmac-sha256` or `rsa-sha256` is assumed if other configuration options for that type are set -|`inbound.keys.*.hmac.secret` |{nbsp} |Secret shared by the service that signed the request and this service for `hmac-sha256` algorithm -|`inbound.keys.*.public-key` |{nbsp} |Public key configuration, implies `rsa-sha256` algorithm -|`inbound.keys.*.public-key.keystore` |{nbsp} |Keystore configuration for public key - full configuration as defined by `KeyStore` class -|`outbound` |{nbsp} |A list of outbound configurations -|`outbound.*.name` |{nbsp} |Required name of outbound configuration -|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated -|`outbound.*.password` |{nbsp} |Optional password used for outbound security -|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for -|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression -|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression -|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for -|`outbound.*.signature` |{nbsp} |Configuration related to outbound signature configuration -|`outbound.*.signature.key-id` |{nbsp} |Key id to use in the outbound signature (to map to appropriate public key in target service's configuration) -|`outbound.*.signature.hmac.secret` |{nbsp} |Shared secret for hmac -|`outbound.*.signature.private-key` |{nbsp} |Private key configuration for rsa based signatures -|`outbound.*.signature.private-key.keystore` |{nbsp} |Keystore configuration for private key - full configuration as defined by `KeyStore` class -|=== - ==== Signature basics * standard: based on https://tools.ietf.org/html/draft-cavage-http-signatures-03 diff --git a/docs/includes/security/providers/idcs-role-mapper.adoc b/docs/includes/security/providers/idcs-role-mapper.adoc index 0fe829c7344..463689e6d4e 100644 --- a/docs/includes/security/providers/idcs-role-mapper.adoc +++ b/docs/includes/security/providers/idcs-role-mapper.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === IDCS Role Mapper :description: Helidon Security IDCS Role Mapper Provider :keywords: helidon, security, idcs +:feature-name: IDCS Role Mapper Security Provider A role mapper to retrieve roles from Oracle IDCS. @@ -35,17 +36,13 @@ A role mapper to retrieve roles from Oracle IDCS. ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider ----- +==== Single-tenant IDCS Role Mapper -[source,text] -.Provider configuration key ----- -idcs-role-mapper ----- +include::{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperRxProvider.adoc[leveloffset=+2,tag=config] + +==== Multi-tenant IDCS Role Mapper + +include::{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperRxProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -64,32 +61,6 @@ security: identity-uri: "IDCS identity server address" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`multitenant` |`true` |Whether to support multi-tenancy with this provider -|`idcs-tenant-handler` |Header `X-USER-IDENTITY-SERVICE-GUID` |Multi-tenant specific `TokenHandler` configuration to retrieve the tenant id -|`idcs-app-name-handler` |Header `X-RESOURCE-SERVICE-INSTANCE-IDENTITY-APPNAME` |Multi-tenant specific `TokenHandler` configuration to retrieve the application name -|`cache-config` |{nbsp} |Configuration of cache of roles for subjects -|`cache-config.cache-enabled` |`true` |Possibility to disable the cache altogether -|`cache-config.max-size` |`100_000` |Maximal number of records in the cache -|`cache-config.cache-timeout-millis` |1 hour |Cache timeout in milliseconds -|`cache-config.cache-evict-delay-millis` |1 minute |How long to wait before starting the first eviction process -|`cache-config.cache-evict-period-millis` |5 minutes |Period of running the eviction process -|`cache-config.parallelism-threshold` |`10_000` |Threshold as used by `ConcurrentHashMap.forEachKey` -|`cache-config.evictor-class` |{nbsp} |Implementation of `BiFunction` that receives key and value, and returns `true` for records that should be removed - from the cache. Eviction mechanism should be fast, as it is called within methods of `ConcurrentHashMap` -|`subject-types` |`USER` |Can use `USER` and/or `SERVICE` -|`default-idcs-subject-type` |`user` |Default subject type to use when requesting roles, can be `user` or `client` -|`oidc-config` |{nbsp} |`OidcConfig` configuration, except `validate-with-jwk` is set to `false`, - and `server-type` is set to `idcs` -|=== - ==== How does it work? The provider asks the IDCS server to provide list of roles for the currently authenticated user. diff --git a/docs/includes/security/providers/jwt.adoc b/docs/includes/security/providers/jwt.adoc index 7b29360757d..26860e5e0e3 100644 --- a/docs/includes/security/providers/jwt.adoc +++ b/docs/includes/security/providers/jwt.adoc @@ -21,6 +21,7 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] === JWT Provider :description: Helidon Security JWT Provider :keywords: helidon, security, jwt +:feature-name: JWT Security Provider JWT token authentication and outbound security provider. @@ -35,17 +36,9 @@ JWT token authentication and outbound security provider. ---- -[source,text] -.Provider class name ----- -io.helidon.security.providers.jwt.JwtProvider ----- +==== Overview -[source,text] -.Provider configuration key ----- -jwt ----- +include::{rootdir}/config/io_helidon_security_providers_jwt_JwtProvider.adoc[leveloffset=+2,tag=config] ==== Example code @@ -73,44 +66,6 @@ security: jwt-audience: "http://1.partner-service" ---- -==== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is - an important distinction when more than one provider is used -|`authenticate` |`true` |Whether to attempt authentication -|`propagate` |`true` |Whether to attempt identity propagation/JWT creation -|`principal-type` |`USER` |Whether we authenticate a user or a service (other option is SERVICE) -|`atn-token` | |A group for configuring authentication of the request -|`atn-token.verify-signature` |`true` |Whether to verify signature in incoming JWT. If disabled, _ANY_ JWT will be accepted -|`atn-token.jwt-audience` |{nbsp} |Expected audience of the JWT. If not defined, any audience is accepted (and we may accept JWT not inteded for us) -|`atn-token.jwk.resource.*` |{nbsp} |Configuration of the JWK to obtain key(s) to validate signatures of inbound token. The JWK should contain public keys. This may be: jwk.resource.path, jwk.resource.resource-path, jwk.resource.url, jwk.resource.content-plain (actual JSON string), jwk.resource.content (base64) -|`atn-token.handler` |`Authorization` header with `bearer ` prefix |A handler configuration for inbound token - e.g. how to extract it -|`atn-token.handler.header` |{nbsp} |Name of a header the token is expected in -|`atn-token.handler.prefix` |{nbsp} |Prefix before the token value (optional) -|`atn-token.handler.regexp` |{nbsp} |Regular expression to obtain the token, first matching group is used (optional) -|`sign-token` |{nbsp} |A group for configuring outbound security -|`sign-token.jwk.resource.*` |{nbsp} |Configuration of the JWK to use when generating tokens (follows same rules as atn-token.jwk above), this JWK must contain private keys when using asymmetric ciphers -|`sign-token.jwt-issuer` |{nbsp} |When we issue a new token, this is the issuer to be placed into it (validated by target service) -|`sign-token.outbound` |{nbsp} |A group for configuring outbound rules (based on transport, host and.or path) -|`sign-token.outbound.*.name` |{nbsp} |A short descriptive name for configured target service(s) -|`sign-token.outbound.*.transports` |any |An array of transports this outbound matches (e.g. https) -|`sign-token.outbound.*.hosts` |any |An array of hosts this outbound matches, may use * as a wild-card (e.g. *.oracle.com) -|`sign-token.outbound.*.paths` |any |An array of paths on the host this outbound matches, may use * as a wild-card (e.g. /some/path/*) -|`sign-token.outbound.*.outbound-token` |`Authorization` header with `bearer ` prefix |Configuration of outbound token handler (same as atn-token.handler) -|`sign-token.outbound.*.outbound-token.format` |{nbsp} |Java text format for generating the value of outbound token header (e.g. "bearer %1$s") -|`sign-token.outbound.*.jwk-kid` |{nbsp} |If this key is defined, we are generating a new token, otherwise we propagate existing. Defines the key id of a key definition in the JWK file to use for signing the outbound token -|`sign-token.outbound.*.jwt-kid` |{nbsp} |A key to use in the generated JWT - this is for the other service to locate the verification key in their JWK -|`sign-token.outbound.*.jwt-audience` |{nbsp} |Audience this key is generated for (e.g. http://www.example.org/api/myService) - validated by the other service -|`sign-token.outbound.*.jwt-not-before-seconds` |`5` |Makes this key valid this amount of seconds into the past. Allows a certain time-skew for the generated token to be valid before current time (e.g. when we expect a certain misalignment of clocks) -|`sign-token.outbound.*.jwt-validity-seconds` |1 day |Token validity in seconds -|=== - ==== How does it work? JSON Web Token (JWT) provider has support for authentication and outbound security. diff --git a/docs/includes/security/providers/oidc.adoc b/docs/includes/security/providers/oidc.adoc index 4e0951af3fe..5636d501122 100644 --- a/docs/includes/security/providers/oidc.adoc +++ b/docs/includes/security/providers/oidc.adoc @@ -21,20 +21,11 @@ ifndef::rootdir[:rootdir: {docdir}/../../..] :basic-table-intro: The table below lists the configuration keys that identify the CORS characteristics. :cors-config-key-explanation: , identified with the configuration key 'cors', :cors-config-table-exclude-methods: true +:feature-name: OIDC Security Provider -[source,text] -.Provider class name ----- -io.helidon.security.providers.oidc.OidcProvider ----- - -[source,text] -.Provider configuration key ----- -oidc ----- +include::{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[leveloffset=+2,tag=config] -== Example code +=== Example code See the link:{helidon-github-tree-url}/examples/security/idcs-login[example] on GitHub. [source,yaml] @@ -58,68 +49,7 @@ security: header: "X-Internal-Auth" ---- -== Configuration options -The following table shows all configuration options of the provider and their default values - -[cols="2,2,5"] - -|=== -|key |default value |description - -|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is -an important distinction when more than one provider is used -|`client-id` |{nbsp} |Client ID as generated by identity server -|`client-secret` |{nbsp} |Client secret as generated by identity server -|`identity-uri` |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata -|`frontend-uri` |{nbsp} |Full URI of this service for redirects back from OIDC server -|`issuer` |`issuer` from OIDC metadata |Issuer of token - each JWT is validated to check the issuer -|`audience` | {nbsp} |Audience of a token - each JWT is validated to check the audience -|`cors` | {nbsp} | Cross-origin resource sharing settings (see <> below) -|`proxy-protocol` |`http` |Proxy protocol to use when proxy is used -|`proxy-host` |`null` |Proxy host to use. When defined, triggers usage of proxy for HTTP requests -|`proxy-port` |`80` |Port of the proxy server to use -|`redirect-uri` |`/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. -|`scope-audience` |empty string |Audience of the scope required by this application. This is prefixed to the scope name when requesting scopes from the identity server. -|`cookie-use` |`true` |Whether to use cookie to store JWT. If used, redirects happen only in case the user is not authenticated or has insufficient scopes -|`cookie-name` |`JSESSIONID` |Name of the cookie -|`cookie-domain` |{nbsp} |Domain the cookie is valid for. Not used by default -|`cookie-path` |`/` |Path the cookie is valid for. -|`cookie-max-age-seconds` |{nsbp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long the cookie is valid. -|`cookie-http-only` |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured. -|`cookie-secure` |`false` |When using cookie, if set to true, the Secure attribute will be configured. -|`cookie-same-site` |`Lax` |When using cookie, used to set the SameSite cookie value. Can be "Strict" or "Lax". Setting this to "Strict" will result in infinite redirects when calling OIDC on a different host. -|`query-param-use` |`false` |Whether to expect JWT in a query parameter -|`query-param-name` |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used. -|`header-use` |`false` |Whether to expect JWT in a header field. -|`header-token` |`Authorization` header with prefix `bearer` |A TokenHandler configuration to process header containing a JWT -|`oidc-metadata-well-known` |`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. token-endpoint-uri). -|`oidc-metadata.resource` |`identity-uri/.well-known/openid-configuration` |Resource configuration for OIDC Metadata containing endpoints to various identity services, as well as information about the identity server. See Resource.create(io.helidon.config.Config) -|`token-endpoint-uri` |`token_endpoint` in OIDC metadata, or `identity-url/oauth2/v1/token` if not available |URI of a token endpoint used to obtain a JWT based on the authentication code. -|`authorization-endpoint-uri` |"authorization_endpoint" in OIDC metadata, or `identity-uri/oauth2/v1/authorize` if not available |URI of an authorization endpoint used to redirect users to for logging-in. -|`validate-with-jwk` |`true` |When true - validate against jwk defined by "sign-jwk", when false validate JWT through OIDC Server endpoint "validation-endpoint-uri" -|`sign-jwk.resource` |"jwks-uri" in OIDC metadata, or `identity-uri/admin/v1/SigningCert/jwk` if not available, only needed when jwt validation is done by us |A resource pointing to JWK with public keys of signing certificates used to validate JWT. See Resource.create(io.helidon.config.Config) -|`introspect-endpoint-uri` |"introspection_endpoint" in OIDC metadata, or `identity-uri/oauth2/v1/introspect` |When validate-with-jwk is set to "false", this is the endpoint used -|`base-scopes` |`openid` |Configure scopes to be requested by default. If the scope has a qualifier, it must be included here -|`redirect` |`true` |Whether to redirect to identity server when authentication failed. -|`realm` |`helidon` |Realm returned in HTTP response if redirect is not enabled or possible. -|`redirect-attempt-param` |`h_ra` |Query parameter holding the number of times we redirected to an identity server. Customizable to prevent conflicts with application parameters -|`max-redirects` |`5` |Maximal number of times we can redirect to an identity server. When the number is reached, no further redirects happen and the request finishes with an error (status 401) -|`server-type` |{nbsp} |Type of identity server. Currently supported is idcs or not configured (for default). -|`propagate` |{nbsp} |Whether to propagate the token we have. Defaults to `false` unless an outbound configuration is defined -|`outbound` |{nbsp} |A list of outbound configurations -|`outbound.*.name` |{nbsp} |Required name of outbound configuration -|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for -|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression -|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression -|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for -|`outbound.*.outbound-token` |`Authorization` header with `bearer` prefix |Configuration of outbound header used to propagate -|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token -|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"bearer"` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`) -|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"bearer %1s"` -|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"bearer (.*)"` -|=== - -== How does it work? +=== How does it work? At Helidon startup, if OIDC provider is configured, the following will happen: 1. `client-id`, `client-secret`, and `identityUri` are validated - these must provide values diff --git a/docs/se/security/providers.adoc b/docs/se/security/providers.adoc index 6321ba0f29a..c20c91f6dce 100644 --- a/docs/se/security/providers.adoc +++ b/docs/se/security/providers.adoc @@ -19,10 +19,16 @@ = Security Providers :description: Helidon Security providers :keywords: helidon, security +:feature-name: Security Providers :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== Contents + +- <> +- <> + == Implemented Security Providers Helidon provides the following security providers for endpoint protection: @@ -62,6 +68,8 @@ Open ID Connect security provider. ---- +==== Overview + In Helidon SE, we need to register the redirection support with routing (in addition to `WebSecurity` that integrates with `WebServer`). This is not required when `redirect` is set to false. @@ -93,3 +101,14 @@ include::{rootdir}/includes/security/providers/google-login.adoc[] include::{rootdir}/includes/security/providers/jwt.adoc[] +== Reference + +* link:{https://github.com/oracle/helidon/tree/master/examples/security[Helidon Security Examples] +* link:{security-provider-oidc-base-url}/module-summary.html[Helidon OIDC JavaDoc] +* link:{security-provider-httpauth-base-url}/module-summary.html[Helidon HTTP Authentication JavaDoc] +* link:{security-provider-header-base-url}/module-summary.html[Helidon Header Authentication JavaDoc] +* link:{security-provider-httpsign-base-url}/module-summary.html[Helidon HTTP Signature JavaDoc] +* link:{security-provider-idcs-mapper-base-url}/module-summary.html[Helidon IDCS Role Mapper JavaDoc] +* link:{security-provider-abac-base-url}/module-summary.html[Helidon ABAC JavaDoc] +* link:{security-provider-google-login-base-url}/module-summary.html[Helidon Google Login JavaDoc] +* link:{security-provider-jwt-base-url}/module-summary.html[Helidon JWT JavaDoc] \ No newline at end of file diff --git a/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProvider.java b/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProvider.java index 3658a0aba33..a6ee33727fe 100644 --- a/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProvider.java +++ b/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -59,7 +59,6 @@ * @see #create(Config) */ public final class AbacProvider extends SynchronousProvider implements AuthorizationProvider { - private static final String CONFIG_KEY = "abac"; private final List> validators = new ArrayList<>(); private final Set> supportedAnnotations; @@ -142,7 +141,7 @@ protected AuthorizationResponse syncAuthorize(ProviderRequest providerRequest) { // list all custom objects and check those that implement AttributeConfig and ... validateCustom(epConfig, collector); - Optional abacConfig = epConfig.config(CONFIG_KEY); + Optional abacConfig = epConfig.config(AbacProviderService.PROVIDER_CONFIG_KEY); for (var validator : validators) { // order of preference - explicit class, configuration, annotation @@ -239,7 +238,7 @@ private void validateCustom(EndpointConfig epConfig, Errors.Collector collector) } private void validateConfig(EndpointConfig config, Errors.Collector collector) { - config.config(CONFIG_KEY) + config.config(AbacProviderService.PROVIDER_CONFIG_KEY) .ifPresent(abacConfig -> validateAbacConfig(abacConfig, collector)); } @@ -336,7 +335,7 @@ private boolean isSupportedAnnotation(Class type) { /** * A fluent API builder for {@link AbacProvider}. */ - @Configured(prefix = "abac", + @Configured(prefix = AbacProviderService.PROVIDER_CONFIG_KEY, description = "Attribute Based Access Control provider", provides = {SecurityProvider.class, AuthorizationProvider.class}) public static final class Builder implements io.helidon.common.Builder { diff --git a/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProviderService.java b/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProviderService.java index b95a5ad5b3a..b1ea8a72c0b 100644 --- a/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProviderService.java +++ b/security/providers/abac/src/main/java/io/helidon/security/providers/abac/AbacProviderService.java @@ -24,9 +24,12 @@ * A java service to register this provider with {@link io.helidon.security.Security}. */ public class AbacProviderService implements SecurityProviderService { + + static final String PROVIDER_CONFIG_KEY = "abac"; + @Override public String providerConfigKey() { - return "abac"; + return PROVIDER_CONFIG_KEY; } @Override diff --git a/security/providers/common/src/main/java/io/helidon/security/providers/common/EvictableCache.java b/security/providers/common/src/main/java/io/helidon/security/providers/common/EvictableCache.java index e0dfa8b6f8a..6185f806ce4 100644 --- a/security/providers/common/src/main/java/io/helidon/security/providers/common/EvictableCache.java +++ b/security/providers/common/src/main/java/io/helidon/security/providers/common/EvictableCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -24,6 +24,8 @@ import java.util.function.Supplier; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; /** * Generic cache with eviction support. @@ -185,6 +187,7 @@ default void close() { * @param types of keys used in the cache * @param types of values used in the cache */ + @Configured class Builder implements io.helidon.common.Builder, EvictableCache> { private boolean cacheEnabled = true; private long cacheTimeout = CACHE_TIMEOUT_MINUTES; @@ -219,6 +222,7 @@ public EvictableCache build() { * @param timeoutUnit timeout unit * @return updated builder instance */ + @ConfiguredOption(key = "cache-timeout-millis", value = "3600000", type = Long.class) public Builder timeout(long timeout, TimeUnit timeoutUnit) { this.cacheTimeout = timeout; this.cacheTimeoutUnit = timeoutUnit; @@ -232,6 +236,7 @@ public Builder timeout(long timeout, TimeUnit timeoutUnit) { * @param timeoutUnit timeout unit * @return updated builder instance */ + @ConfiguredOption(key = "cache-overall-timeout-millis", value = "3600000", type = Long.class) public Builder overallTimeout(long timeout, TimeUnit timeoutUnit) { this.overallTimeout = timeout; this.overallTimeoutUnit = timeoutUnit; @@ -244,6 +249,7 @@ public Builder overallTimeout(long timeout, TimeUnit timeoutUnit) { * @param cacheMaxSize maximal number of records to store in the cache * @return updated builder instance */ + @ConfiguredOption("100000") public Builder maxSize(long cacheMaxSize) { this.cacheMaxSize = cacheMaxSize; return this; @@ -257,6 +263,10 @@ public Builder maxSize(long cacheMaxSize) { * @param evictTimeUnit time unit to use for these values * @return updated builder instance */ + @ConfiguredOption(key = "cache-evict-delay-millis", value = "60000", type = Long.class, + description = "Delay from the creation of the cache to first eviction") + @ConfiguredOption(key = "cache-evict-period-millis", value = "300000", type = Long.class, + description = "How often to evict records") public Builder evictSchedule(long evictDelay, long evictPeriod, TimeUnit evictTimeUnit) { this.cacheEvictDelay = evictDelay; this.cacheEvictPeriod = evictPeriod; @@ -270,6 +280,7 @@ public Builder evictSchedule(long evictDelay, long evictPeriod, TimeUnit e * @param parallelismThreshold see {@link ConcurrentHashMap#forEachKey(long, Consumer)} * @return updated builder instance */ + @ConfiguredOption("10000") public Builder parallelismThreshold(long parallelismThreshold) { this.parallelismThreshold = parallelismThreshold; return this; @@ -284,6 +295,7 @@ public Builder parallelismThreshold(long parallelismThreshold) { * that should stay in cache * @return updated builder instance */ + @ConfiguredOption(key = "evictor-class", type = Class.class) public Builder evictor(BiFunction evictor) { this.evictor = evictor; return this; @@ -296,6 +308,7 @@ public Builder evictor(BiFunction evictor) { * @param cacheEnabled whether to enable this cache or not (true - enabled by default) * @return updated builder instance */ + @ConfiguredOption("true") public Builder cacheEnabled(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; return this; diff --git a/security/providers/google-login/pom.xml b/security/providers/google-login/pom.xml index f08defae8df..86b16b6fdc5 100644 --- a/security/providers/google-login/pom.xml +++ b/security/providers/google-login/pom.xml @@ -46,6 +46,18 @@ com.google.api-client google-api-client + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + io.helidon.config helidon-config-encryption diff --git a/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenProvider.java b/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenProvider.java index 67d953ac5e0..fa0d4822394 100644 --- a/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenProvider.java +++ b/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenProvider.java @@ -34,6 +34,8 @@ import java.util.logging.Logger; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.EndpointConfig; import io.helidon.security.OutboundSecurityResponse; @@ -47,6 +49,7 @@ import io.helidon.security.providers.common.TokenCredential; import io.helidon.security.spi.AuthenticationProvider; import io.helidon.security.spi.OutboundSecurityProvider; +import io.helidon.security.spi.SecurityProvider; import io.helidon.security.spi.SynchronousProvider; import io.helidon.security.util.TokenHandler; import io.helidon.tracing.Span; @@ -409,7 +412,13 @@ private Instant toInstant(Long epochSeconds) { /** * Fluent API builder to build {@link GoogleTokenProvider} instance. */ + @Configured(prefix = GoogleTokenService.CONFIG_PROVIDER_KEY, + description = "Google Authentication provider", + provides = {SecurityProvider.class, AuthenticationProvider.class}) public static final class Builder implements io.helidon.common.Builder { + + private static final String DEFAULT_REALM_VALUE = "helidon"; + private String clientId; private String proxyHost; @@ -418,7 +427,7 @@ public static final class Builder implements io.helidon.common.Builder tokenParser; private boolean optional; @@ -451,6 +460,7 @@ Builder tokenParser(BiFunction tokenParser) * @param clientId client id as obtained from Google developer console * @return updated builder instance */ + @ConfiguredOption public Builder clientId(String clientId) { Objects.requireNonNull(clientId); @@ -465,6 +475,7 @@ public Builder clientId(String clientId) { * @param optional whether to be optional or not * @return updated builder instance */ + @ConfiguredOption("false") public Builder optional(boolean optional) { this.optional = optional; return this; @@ -477,6 +488,7 @@ public Builder optional(boolean optional) { * @param provider token provider * @return updated builder instance */ + @ConfiguredOption(key = "token", value = "`Authorization` header with `bearer` prefix") public Builder tokenProvider(TokenHandler provider) { this.tokenHandler = provider; return this; @@ -488,6 +500,7 @@ public Builder tokenProvider(TokenHandler provider) { * @param realm realm of authentication * @return updated builder instance */ + @ConfiguredOption(DEFAULT_REALM_VALUE) public Builder realm(String realm) { this.realm = realm; return this; @@ -499,6 +512,7 @@ public Builder realm(String realm) { * @param host host of http proxy server * @return updated builder instance */ + @ConfiguredOption public Builder proxyHost(String host) { if ((null == host) || host.isEmpty()) { this.proxyHost = null; @@ -514,6 +528,7 @@ public Builder proxyHost(String host) { * @param port port of http proxy server, defaults to 80 * @return updated builder instance */ + @ConfiguredOption("80") public Builder proxyPort(int port) { this.proxyPort = port; return this; @@ -545,6 +560,7 @@ public Builder config(Config config) { * @param outboundConfig configuration of outbound * @return updated builder instance */ + @ConfiguredOption(key = "outbound") public Builder outboundConfig(OutboundConfig outboundConfig) { this.outboundConfig = outboundConfig; return this; diff --git a/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenService.java b/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenService.java index fc6ad4f94df..dbd8f41037e 100644 --- a/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenService.java +++ b/security/providers/google-login/src/main/java/io/helidon/security/providers/google/login/GoogleTokenService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -24,9 +24,12 @@ * Java service ({@link SecurityProviderService}) for google token provider. */ public class GoogleTokenService implements SecurityProviderService { + + static final String CONFIG_PROVIDER_KEY = "google-login"; + @Override public String providerConfigKey() { - return "google-login"; + return CONFIG_PROVIDER_KEY; } @Override diff --git a/security/providers/google-login/src/main/java/module-info.java b/security/providers/google-login/src/main/java/module-info.java index 15960d96827..317510ddbb8 100644 --- a/security/providers/google-login/src/main/java/module-info.java +++ b/security/providers/google-login/src/main/java/module-info.java @@ -29,6 +29,8 @@ requires io.helidon.security.util; requires io.helidon.tracing; + requires static io.helidon.config.metadata; + exports io.helidon.security.providers.google.login; provides io.helidon.security.spi.SecurityProviderService with io.helidon.security.providers.google.login.GoogleTokenService; diff --git a/security/providers/header/pom.xml b/security/providers/header/pom.xml index 9fd4c42a508..37d4dd80c21 100644 --- a/security/providers/header/pom.xml +++ b/security/providers/header/pom.xml @@ -38,6 +38,18 @@ io.helidon.security helidon-security-util + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + io.helidon.config helidon-config-encryption diff --git a/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnProvider.java b/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnProvider.java index 83cb5717e54..92581d3a336 100644 --- a/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnProvider.java +++ b/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -22,6 +22,8 @@ import java.util.Optional; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.EndpointConfig; import io.helidon.security.OutboundSecurityResponse; @@ -34,6 +36,7 @@ import io.helidon.security.providers.common.OutboundTarget; import io.helidon.security.spi.AuthenticationProvider; import io.helidon.security.spi.OutboundSecurityProvider; +import io.helidon.security.spi.SecurityProvider; import io.helidon.security.spi.SynchronousProvider; import io.helidon.security.util.TokenHandler; @@ -178,6 +181,9 @@ private OutboundSecurityResponse respond(SecurityEnvironment outboundEnv, /** * A fluent api Builder for {@link HeaderAtnProvider}. */ + @Configured(prefix = HeaderAtnService.PROVIDER_CONFIG_KEY, + description = "Security provider that extracts a username (or service name) from a header.", + provides = {SecurityProvider.class, AuthenticationProvider.class}) public static final class Builder implements io.helidon.common.Builder { private final OutboundConfig.Builder outboundBuilder = OutboundConfig.builder(); @@ -232,6 +238,7 @@ public Builder config(Config config) { * @param subjectType type of principal * @return updated builder instance */ + @ConfiguredOption(key = "principal-type", value = "USER") public Builder subjectType(SubjectType subjectType) { this.subjectType = subjectType; @@ -252,6 +259,7 @@ public Builder subjectType(SubjectType subjectType) { * @param propagate whether to propagate identity (true) or not (false) * @return updated builder instance */ + @ConfiguredOption("false") public Builder propagate(boolean propagate) { this.propagate = propagate; return this; @@ -263,6 +271,7 @@ public Builder propagate(boolean propagate) { * @param authenticate whether to authenticate (true) or not (false) * @return updated builder instance */ + @ConfiguredOption("true") public Builder authenticate(boolean authenticate) { this.authenticate = authenticate; return this; @@ -274,6 +283,7 @@ public Builder authenticate(boolean authenticate) { * @param tokenHandler token handler instance * @return updated builder instance */ + @ConfiguredOption(key = "atn-token") public Builder atnTokenHandler(TokenHandler tokenHandler) { this.atnTokenHandler = tokenHandler; @@ -287,6 +297,7 @@ public Builder atnTokenHandler(TokenHandler tokenHandler) { * @param tokenHandler token handler instance * @return updated builder instance */ + @ConfiguredOption(key = "outbound-token") public Builder outboundTokenHandler(TokenHandler tokenHandler) { this.outboundTokenHandler = tokenHandler; @@ -301,6 +312,7 @@ public Builder outboundTokenHandler(TokenHandler tokenHandler) { * @param optional whether authentication is optional (true) or required (false) * @return updated builder instance */ + @ConfiguredOption("false") public Builder optional(boolean optional) { this.optional = optional; return this; @@ -312,6 +324,7 @@ public Builder optional(boolean optional) { * @param target outbound target * @return updated builder instance */ + @ConfiguredOption(key = "outbound", kind = ConfiguredOption.Kind.LIST) public Builder addOutboundTarget(OutboundTarget target) { this.outboundBuilder.addTarget(target); return this; diff --git a/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnService.java b/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnService.java index c6494c276aa..d2e2ece80bc 100644 --- a/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnService.java +++ b/security/providers/header/src/main/java/io/helidon/security/providers/header/HeaderAtnService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,9 +25,12 @@ * with {@link io.helidon.security.Security}. */ public class HeaderAtnService implements SecurityProviderService { + + static final String PROVIDER_CONFIG_KEY = "header-atn"; + @Override public String providerConfigKey() { - return "header-atn"; + return PROVIDER_CONFIG_KEY; } @Override diff --git a/security/providers/header/src/main/java/module-info.java b/security/providers/header/src/main/java/module-info.java index 03ce3481ae3..c9f1750d237 100644 --- a/security/providers/header/src/main/java/module-info.java +++ b/security/providers/header/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -24,6 +24,8 @@ requires io.helidon.security.util; requires io.helidon.security.providers.common; + requires static io.helidon.config.metadata; + exports io.helidon.security.providers.header; provides io.helidon.security.spi.SecurityProviderService with io.helidon.security.providers.header.HeaderAtnService; diff --git a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpBasicAuthProvider.java b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpBasicAuthProvider.java index e1612701295..78ecee57e70 100644 --- a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpBasicAuthProvider.java +++ b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpBasicAuthProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -451,6 +451,7 @@ public Builder optional(boolean optional) { * @param target outbound target * @return updated builder instance */ + @ConfiguredOption(key = "outbound", kind = ConfiguredOption.Kind.LIST) public Builder addOutboundTarget(OutboundTarget target) { this.outboundBuilder.addTarget(target); return this; diff --git a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthProvider.java b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthProvider.java index 07ffe721cdd..d2f04a763e4 100644 --- a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthProvider.java +++ b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -34,6 +34,8 @@ import javax.crypto.Cipher; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.Principal; import io.helidon.security.ProviderRequest; @@ -43,6 +45,7 @@ import io.helidon.security.Subject; import io.helidon.security.SubjectType; import io.helidon.security.spi.AuthenticationProvider; +import io.helidon.security.spi.SecurityProvider; import io.helidon.security.spi.SynchronousProvider; /** @@ -269,7 +272,12 @@ private Subject buildSubject(SecureUserStore.User user) { /** * {@link HttpDigestAuthProvider} fluent API builder. */ + @Configured(prefix = HttpDigestAuthService.PROVIDER_CONFIG_KEY, + description = "Http digest authentication security provider", + provides = {SecurityProvider.class, AuthenticationProvider.class}) public static final class Builder implements io.helidon.common.Builder { + + private static final String DEFAULT_REALM = "Helidon"; private static final SecureUserStore EMPTY_STORE = login -> Optional.empty(); /** * Default is 24 hours. @@ -278,7 +286,7 @@ public static final class Builder implements io.helidon.common.Builder digestQopOptions = new LinkedList<>(); private SecureUserStore userStore = EMPTY_STORE; private boolean optional = false; - private String realm = "Helidon"; + private String realm = DEFAULT_REALM; private SubjectType subjectType = SubjectType.USER; private HttpDigest.Algorithm digestAlgorithm = HttpDigest.Algorithm.MD5; private boolean noDigestQop = false; @@ -290,6 +298,7 @@ private Builder() { /** * Update builder from configuration. + * * @param config to read configuration from, located on the node of the provider * @return updated builder instance */ @@ -342,6 +351,7 @@ public HttpDigestAuthProvider build() { * @param subjectType type of principal * @return updated builder instance */ + @ConfiguredOption(key = "principal-type", value = "USER") public Builder subjectType(SubjectType subjectType) { this.subjectType = subjectType; @@ -362,6 +372,7 @@ public Builder subjectType(SubjectType subjectType) { * @param store User store to use * @return updated builder instance */ + @ConfiguredOption(key = "users", type = ConfigUserStore.ConfigUser.class, kind = ConfiguredOption.Kind.LIST) public Builder userStore(SecureUserStore store) { this.userStore = store; return this; @@ -375,6 +386,7 @@ public Builder userStore(SecureUserStore store) { * @param optional whether authentication is optional (true) or required (false) * @return updated builder instance */ + @ConfiguredOption("false") public Builder optional(boolean optional) { this.optional = optional; return this; @@ -386,6 +398,7 @@ public Builder optional(boolean optional) { * @param realm security realm name to send to browser (or any other client) when unauthenticated * @return updated builder instance */ + @ConfiguredOption(DEFAULT_REALM) public Builder realm(String realm) { this.realm = realm; return this; @@ -397,6 +410,7 @@ public Builder realm(String realm) { * @param algorithm Algorithm to use, default is {@link HttpDigest.Algorithm#MD5} * @return updated builder instance */ + @ConfiguredOption(key = "algorithm", value = "MD5") public Builder digestAlgorithm(HttpDigest.Algorithm algorithm) { this.digestAlgorithm = algorithm; return this; @@ -410,6 +424,11 @@ public Builder digestAlgorithm(HttpDigest.Algorithm algorithm) { * @param unit Duration time unit * @return updated builder instance */ + @ConfiguredOption(key = "nonce-timeout-millis", + type = Long.class, + value = "86400000", + description = "How long will the nonce value be valid. When timed-out, " + + "browser will re-request username/password.") public Builder digestNonceTimeout(long duration, TimeUnit unit) { this.digestNonceTimeoutMillis = unit.toMillis(duration); return this; @@ -425,6 +444,7 @@ public Builder digestNonceTimeout(long duration, TimeUnit unit) { * @param serverSecret a password to encrypt our nonce values with * @return updated builder instance */ + @ConfiguredOption(key = "server-secret", type = String.class) public Builder digestServerSecret(char[] serverSecret) { this.digestServerSecret = Arrays.copyOf(serverSecret, serverSecret.length); @@ -437,6 +457,10 @@ public Builder digestServerSecret(char[] serverSecret) { * @param qop qop to add to list of supported qops * @return updated builder instance */ + @ConfiguredOption(key = "qop", + value = "NONE", + description = "Only `AUTH` supported. If left empty," + + " uses the legacy approach (older RFC version). `AUTH-INT` is not supported.") public Builder addDigestQop(HttpDigest.Qop qop) { this.digestQopOptions.add(qop); return this; diff --git a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthService.java b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthService.java index d777838c11b..41d0d3af799 100644 --- a/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthService.java +++ b/security/providers/http-auth/src/main/java/io/helidon/security/providers/httpauth/HttpDigestAuthService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -24,9 +24,11 @@ * Digest authentication service. */ public class HttpDigestAuthService implements SecurityProviderService { + static final String PROVIDER_CONFIG_KEY = "http-digest-auth"; + @Override public String providerConfigKey() { - return "http-digest-auth"; + return PROVIDER_CONFIG_KEY; } @Override diff --git a/security/providers/http-auth/src/main/java/module-info.java b/security/providers/http-auth/src/main/java/module-info.java index ba04822507e..70dd08169f3 100644 --- a/security/providers/http-auth/src/main/java/module-info.java +++ b/security/providers/http-auth/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,6 +25,7 @@ requires io.helidon.security.providers.common; requires io.helidon.security.util; requires java.logging; + requires static io.helidon.config.metadata; exports io.helidon.security.providers.httpauth; diff --git a/security/providers/http-sign/pom.xml b/security/providers/http-sign/pom.xml index 10afdb0a66b..67c260fb424 100644 --- a/security/providers/http-sign/pom.xml +++ b/security/providers/http-sign/pom.xml @@ -46,6 +46,18 @@ io.helidon.security helidon-security-util + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + io.helidon.security.integration diff --git a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignProvider.java b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignProvider.java index 65ac55d34da..f5429771d4e 100644 --- a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignProvider.java +++ b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignProvider.java @@ -27,6 +27,8 @@ import java.util.concurrent.CompletionStage; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.EndpointConfig; import io.helidon.security.OutboundSecurityResponse; @@ -316,9 +318,15 @@ private OutboundSecurityResponse signRequest(SecurityEnvironment outboundEnv) { /** * Fluent API builder for this provider. Call {@link #build()} to create a provider instance. */ + @Configured(prefix = HttpSignService.PROVIDER_CONFIG_KEY, + description = "HTTP header signature provider.", + provides = {AuthenticationProvider.class}) public static final class Builder implements io.helidon.common.Builder { + + private static final String DEFAULT_REALM_VALUE = "helidon"; + private boolean optional = true; - private String realm = "helidon"; + private String realm = DEFAULT_REALM_VALUE; private final Set acceptHeaders = EnumSet.noneOf(HttpSignHeader.class); private SignedHeadersConfig inboundRequiredHeaders = SignedHeadersConfig.builder().build(); private OutboundConfig outboundConfig = OutboundConfig.builder().build(); @@ -387,6 +395,7 @@ public Builder config(Config config) { * @param targets targets to select correct outbound security * @return updated builder instance */ + @ConfiguredOption(key = "outbound") public Builder outbound(OutboundConfig targets) { this.outboundConfig = targets; return this; @@ -417,6 +426,7 @@ public Builder outbound(OutboundConfig targets) { * @param client a single client configuration for inbound communication * @return updated builder instance */ + @ConfiguredOption(key = "inbound.keys", kind = ConfiguredOption.Kind.LIST) public Builder addInbound(InboundClientDefinition client) { this.inboundKeys.put(client.keyId(), client); return this; @@ -438,6 +448,7 @@ public Builder addInbound(InboundClientDefinition client) { * @param inboundRequiredHeaders headers configuration * @return updated builder instance */ + @ConfiguredOption(key = "sign-headers", type = SignedHeadersConfig.HeadersConfig.class, kind = ConfiguredOption.Kind.LIST) public Builder inboundRequiredHeaders(SignedHeadersConfig inboundRequiredHeaders) { this.inboundRequiredHeaders = inboundRequiredHeaders; return this; @@ -450,6 +461,7 @@ public Builder inboundRequiredHeaders(SignedHeadersConfig inboundRequiredHeaders * @param header header to look for signature * @return updated builder instance */ + @ConfiguredOption(key = "headers", kind = ConfiguredOption.Kind.LIST) public Builder addAcceptHeader(HttpSignHeader header) { this.acceptHeaders.add(header); return this; @@ -464,6 +476,7 @@ public Builder addAcceptHeader(HttpSignHeader header) { * @param optional true for optional singatures * @return updated builder instance */ + @ConfiguredOption("true") public Builder optional(boolean optional) { this.optional = optional; return this; @@ -476,6 +489,7 @@ public Builder optional(boolean optional) { * @param realm realm to challenge with, defautls to "helidon" * @return updated builder instance */ + @ConfiguredOption(DEFAULT_REALM_VALUE) public Builder realm(String realm) { this.realm = realm; return this; @@ -493,6 +507,7 @@ public Builder realm(String realm) { * @param backwardCompatible whether to run in backward compatible mode * @return updated builder instance */ + @ConfiguredOption("false") public Builder backwardCompatibleEol(Boolean backwardCompatible) { this.backwardCompatibleEol = backwardCompatible; return this; diff --git a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignService.java b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignService.java index a7b7a06d92e..fbd26dccd30 100644 --- a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignService.java +++ b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/HttpSignService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,9 +25,12 @@ * automatic loading of this provider from configuration. */ public class HttpSignService implements SecurityProviderService { + + static final String PROVIDER_CONFIG_KEY = "http-signatures"; + @Override public String providerConfigKey() { - return "http-signatures"; + return PROVIDER_CONFIG_KEY; } @Override diff --git a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/InboundClientDefinition.java b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/InboundClientDefinition.java index 902625ccd21..f2c8caf5d06 100644 --- a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/InboundClientDefinition.java +++ b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/InboundClientDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -23,6 +23,8 @@ import io.helidon.common.pki.KeyConfig; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.SubjectType; /** @@ -147,6 +149,7 @@ public Optional hmacSharedSecret() { * Fluent API builder to create a new instance of {@link InboundClientDefinition}. * Use {@link #build()} to create the instance. */ + @Configured public static final class Builder implements io.helidon.common.Builder { private String keyId; private String algorithm; @@ -164,6 +167,7 @@ private Builder() { * @param name name of security principal * @return updated builder instance */ + @ConfiguredOption public Builder principalName(String name) { this.principalName = name; return this; @@ -175,6 +179,7 @@ public Builder principalName(String name) { * @param keyId key id as provided in inbound signature * @return updated builder instance */ + @ConfiguredOption public Builder keyId(String keyId) { this.keyId = keyId; if (this.principalName == null) { @@ -189,6 +194,7 @@ public Builder keyId(String keyId) { * @param type principal type * @return updated builder instance */ + @ConfiguredOption(key = "principal-type", value = "SERVICE") public Builder subjectType(SubjectType type) { this.subjectType = type; return this; @@ -205,6 +211,7 @@ public Builder subjectType(SubjectType type) { * @param algorithm algorithm used * @return updated builder instance */ + @ConfiguredOption public Builder algorithm(String algorithm) { this.algorithm = algorithm; return this; @@ -216,6 +223,7 @@ public Builder algorithm(String algorithm) { * @param keyConfig keys configured to access a public key to validate signature * @return updated builder instance */ + @ConfiguredOption(key = "public-key") public Builder publicKeyConfig(KeyConfig keyConfig) { if (null == algorithm) { algorithm = HttpSignProvider.ALGORITHM_RSA; @@ -249,6 +257,7 @@ public Builder hmacSecret(byte[] secret) { * @param secret shared secret to validate signature * @return updated builder instance */ + @ConfiguredOption(key = "hmac.secret") public Builder hmacSecret(String secret) { return hmacSecret(secret.getBytes(StandardCharsets.UTF_8)); } diff --git a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/SignedHeadersConfig.java b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/SignedHeadersConfig.java index 2c7d88db93a..b915ca525f1 100644 --- a/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/SignedHeadersConfig.java +++ b/security/providers/http-sign/src/main/java/io/helidon/security/providers/httpsign/SignedHeadersConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -23,6 +23,8 @@ import java.util.TreeMap; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; /** * Configuration of required and "if-present" headers to be signed. @@ -163,6 +165,7 @@ public Builder config(String method, HeadersConfig config) { /** * Configuration of headers to be signed. */ + @Configured public static final class HeadersConfig { private final List always; private final List ifPresent; @@ -209,6 +212,14 @@ public static HeadersConfig create(List requiredHeaders, List if * @param config configuration located at header config * @return instance configured from config */ + @ConfiguredOption(key = "always", type = String.class, kind = ConfiguredOption.Kind.LIST, + description = "Headers that must be signed (and signature validation or creation should fail if not " + + "signed or present)") + @ConfiguredOption(key = "if-present", type = String.class, kind = ConfiguredOption.Kind.LIST, + description = "Headers that must be signed if present in request.") + @ConfiguredOption(key = "method", type = String.class, + description = "HTTP method this header configuration is bound to. " + + "If not present, it is considered default header configuration.") public static HeadersConfig create(Config config) { return create(config.get("always").asList(String.class).orElse(List.of()), config.get("if-present").asList(String.class).orElse(List.of())); diff --git a/security/providers/http-sign/src/main/java/module-info.java b/security/providers/http-sign/src/main/java/module-info.java index 1d3cc3084cc..f16d4a46be2 100644 --- a/security/providers/http-sign/src/main/java/module-info.java +++ b/security/providers/http-sign/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -20,6 +20,8 @@ module io.helidon.security.providers.httpsign { requires java.logging; + requires static io.helidon.config.metadata; + requires transitive io.helidon.config; requires transitive io.helidon.common; requires transitive io.helidon.security; diff --git a/security/providers/idcs-mapper/pom.xml b/security/providers/idcs-mapper/pom.xml index 24a67d1571a..7ab34b351e1 100644 --- a/security/providers/idcs-mapper/pom.xml +++ b/security/providers/idcs-mapper/pom.xml @@ -64,6 +64,18 @@ helidon-jersey-media-jsonp provided + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + org.junit.jupiter junit-jupiter-api diff --git a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsMtRoleMapperRxProvider.java b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsMtRoleMapperRxProvider.java index 01007a666bc..82441125833 100644 --- a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsMtRoleMapperRxProvider.java +++ b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsMtRoleMapperRxProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -29,6 +29,8 @@ import io.helidon.common.http.Http; import io.helidon.common.reactive.Single; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.Grant; import io.helidon.security.ProviderRequest; @@ -39,6 +41,7 @@ import io.helidon.security.providers.common.EvictableCache; import io.helidon.security.providers.oidc.common.OidcConfig; import io.helidon.security.spi.SecurityProvider; +import io.helidon.security.spi.SubjectMappingProvider; import io.helidon.security.util.TokenHandler; import io.helidon.webclient.WebClient; import io.helidon.webclient.WebClientRequestBuilder; @@ -336,6 +339,9 @@ public interface MultitenancyEndpoints { * * @param type of a descendant of this builder */ + @Configured(prefix = IdcsRoleMapperProviderService.PROVIDER_CONFIG_KEY, + description = "Multitenant IDCS role mapping provider", + provides = {SecurityProvider.class, SubjectMappingProvider.class}) public static class Builder> extends IdcsRoleMapperRxProviderBase.Builder> implements io.helidon.common.Builder, IdcsMtRoleMapperRxProvider> { @@ -379,6 +385,7 @@ public B config(Config config) { * @param idcsAppNameTokenHandler new token handler to extract IDCS application name * @return updated builder instance */ + @ConfiguredOption(key = "idcs-app-name-handler") public B idcsAppNameTokenHandler(TokenHandler idcsAppNameTokenHandler) { this.idcsAppNameTokenHandler = idcsAppNameTokenHandler; return me; @@ -391,7 +398,7 @@ public B idcsAppNameTokenHandler(TokenHandler idcsAppNameTokenHandler) { * @param idcsTenantTokenHandler new token handler to extract IDCS tenant ID * @return updated builder instance */ - + @ConfiguredOption(key = "idcs-tenant-handler") public B idcsTenantTokenHandler(TokenHandler idcsTenantTokenHandler) { this.idcsTenantTokenHandler = idcsTenantTokenHandler; return me; @@ -414,6 +421,7 @@ public B multitenantEndpoints(MultitenancyEndpoints endpoints) { * @param roleCache cache to use * @return updated builder instance */ + @ConfiguredOption(key = "cache-config", type = EvictableCache.class) public B cache(EvictableCache> roleCache) { this.cache = roleCache; return me; diff --git a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderService.java b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderService.java index a3194e2b53f..23656c844d4 100644 --- a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderService.java +++ b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -23,9 +23,12 @@ * Service for {@link IdcsRoleMapperRxProvider}. */ public class IdcsRoleMapperProviderService implements SecurityProviderService { + + static final String PROVIDER_CONFIG_KEY = "idcs-role-mapper"; + @Override public String providerConfigKey() { - return "idcs-role-mapper"; + return PROVIDER_CONFIG_KEY; } // This is for backward compatibility only. This will be changed in 3.x diff --git a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProvider.java b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProvider.java index d74057d1095..05c38d0a6d8 100644 --- a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProvider.java +++ b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -26,6 +26,8 @@ import io.helidon.common.http.Http; import io.helidon.common.reactive.Single; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.Grant; import io.helidon.security.ProviderRequest; @@ -218,6 +220,9 @@ protected Single> getGrantsFromServer(Subject subject) { * * @param type of builder extending this builder */ + @Configured(prefix = IdcsRoleMapperProviderService.PROVIDER_CONFIG_KEY, + description = "IDCS role mapping provider", + provides = {SecurityProvider.class, SubjectMappingProvider.class}) public static class Builder> extends IdcsRoleMapperRxProviderBase.Builder> implements io.helidon.common.Builder, IdcsRoleMapperRxProvider> { private EvictableCache> roleCache; @@ -264,6 +269,7 @@ public B config(Config config) { * @param roleCache cache to use * @return update builder instance */ + @ConfiguredOption(key = "cache-config", type = EvictableCache.class) public B roleCache(EvictableCache> roleCache) { this.roleCache = roleCache; return me; diff --git a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProviderBase.java b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProviderBase.java index 3df8a473e17..04ed3e0bf19 100644 --- a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProviderBase.java +++ b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperRxProviderBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 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. @@ -33,6 +33,8 @@ import io.helidon.common.http.Http; import io.helidon.common.reactive.Single; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.Grant; import io.helidon.security.ProviderRequest; @@ -260,6 +262,7 @@ private List processServerResponse(JsonObject jsonObject, Strin * Fluent API builder for {@link IdcsRoleMapperRxProviderBase}. * @param Type of the extending builder */ + @Configured public static class Builder> { private final Set supportedTypes = EnumSet.noneOf(SubjectType.class); @@ -313,6 +316,7 @@ public B config(Config config) { * @param config oidc specific configuration, must have at least identity endpoint and client credentials configured * @return updated builder instance */ + @ConfiguredOption public B oidcConfig(OidcConfig config) { this.oidcConfig = config; return me; @@ -347,6 +351,7 @@ public B subjectTypes(SubjectType... types) { * @param subjectType type of subject to use when requesting roles from IDCS * @return updated builder instance */ + @ConfiguredOption(IDCS_SUBJECT_TYPE_USER) public B defaultIdcsSubjectType(String subjectType) { this.defaultIdcsSubjectType = subjectType; return me; @@ -362,6 +367,7 @@ public B defaultIdcsSubjectType(String subjectType) { * @param type subject type to add to the list of supported types * @return updated builder instance */ + @ConfiguredOption(key = "subject-types", kind = ConfiguredOption.Kind.LIST, value = "USER") public B addSubjectType(SubjectType type) { this.supportedTypes.add(type); return me; diff --git a/security/providers/idcs-mapper/src/main/java/module-info.java b/security/providers/idcs-mapper/src/main/java/module-info.java index ab05316ef1e..0244bf67f48 100644 --- a/security/providers/idcs-mapper/src/main/java/module-info.java +++ b/security/providers/idcs-mapper/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,6 +25,8 @@ requires transitive io.helidon.security.jwt; requires transitive io.helidon.security.providers.oidc.common; + requires static io.helidon.config.metadata; + requires io.helidon.security.integration.common; requires io.helidon.security.util; requires io.helidon.webclient; diff --git a/security/providers/jwt/pom.xml b/security/providers/jwt/pom.xml index 66094478ba2..2ea2febf635 100644 --- a/security/providers/jwt/pom.xml +++ b/security/providers/jwt/pom.xml @@ -46,6 +46,18 @@ io.helidon.security helidon-security-util + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + io.helidon.bundles helidon-bundles-config 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 8e6e68d4fc2..eddb04024da 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 @@ -28,6 +28,8 @@ import io.helidon.common.Errors; import io.helidon.common.configurable.Resource; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.security.AuthenticationResponse; import io.helidon.security.EndpointConfig; import io.helidon.security.Grant; @@ -50,6 +52,7 @@ import io.helidon.security.providers.common.TokenCredential; import io.helidon.security.spi.AuthenticationProvider; import io.helidon.security.spi.OutboundSecurityProvider; +import io.helidon.security.spi.SecurityProvider; import io.helidon.security.spi.SynchronousProvider; import io.helidon.security.util.TokenHandler; @@ -592,6 +595,9 @@ public Builder validitySeconds(long validitySeconds) { /** * Fluent API builder for {@link JwtProvider}. */ + @Configured(prefix = JwtProviderService.PROVIDER_CONFIG_KEY, + description = "JWT authentication provider", + provides = {SecurityProvider.class, AuthenticationProvider.class}) public static final class Builder implements io.helidon.common.Builder { private boolean verifySignature = true; private boolean optional = false; @@ -628,6 +634,7 @@ public JwtProvider build() { * @param propagate whether to propagate identity (true) or not (false) * @return updated builder instance */ + @ConfiguredOption("true") public Builder propagate(boolean propagate) { this.propagate = propagate; return this; @@ -639,6 +646,7 @@ public Builder propagate(boolean propagate) { * @param authenticate whether to authenticate (true) or not (false) * @return updated builder instance */ + @ConfiguredOption("true") public Builder authenticate(boolean authenticate) { this.authenticate = authenticate; return this; @@ -652,6 +660,7 @@ public Builder authenticate(boolean authenticate) { * @param allowImpersonation set to true to allow impersonation * @return updated builder instance */ + @ConfiguredOption("false") public Builder allowImpersonation(boolean allowImpersonation) { this.allowImpersonation = allowImpersonation; return this; @@ -668,6 +677,7 @@ public Builder allowImpersonation(boolean allowImpersonation) { * @param allowUnsigned to allow unsigned (insecure) JWT * @return updated builder insdtance */ + @ConfiguredOption("false") public Builder allowUnsigned(boolean allowUnsigned) { this.allowUnsigned = allowUnsigned; return this; @@ -685,6 +695,7 @@ public Builder allowUnsigned(boolean allowUnsigned) { * @param shouldValidate set to false to disable validation of JWT signatures * @return updated builder instance */ + @ConfiguredOption(key = "atn-token.verify-signature", value = "true") public Builder verifySignature(boolean shouldValidate) { this.verifySignature = shouldValidate; return this; @@ -696,6 +707,7 @@ public Builder verifySignature(boolean shouldValidate) { * @param subjectType type of principal * @return updated builder instance */ + @ConfiguredOption(key = "principal-type", value = "USER") public Builder subjectType(SubjectType subjectType) { this.subjectType = subjectType; @@ -716,9 +728,9 @@ public Builder subjectType(SubjectType subjectType) { * @param tokenHandler token handler instance * @return updated builder instance */ + @ConfiguredOption(key = "atn-token.handler") public Builder atnTokenHandler(TokenHandler tokenHandler) { this.atnTokenHandler = tokenHandler; - return this; } @@ -730,6 +742,7 @@ public Builder atnTokenHandler(TokenHandler tokenHandler) { * @param optional whether authentication is optional (true) or required (false) * @return updated builder instance */ + @ConfiguredOption("false") public Builder optional(boolean optional) { this.optional = optional; return this; @@ -742,6 +755,7 @@ public Builder optional(boolean optional) { * to add our configuration. * @return updated builder instance */ + @ConfiguredOption(key = "sign-token") public Builder outboundConfig(OutboundConfig config) { this.outboundConfig = config; return this; @@ -753,6 +767,7 @@ public Builder outboundConfig(OutboundConfig config) { * @param signJwkResource resource pointing to a JSON with keys * @return updated builder instance */ + @ConfiguredOption(key = "sign-token.jwk.resource") public Builder signJwk(Resource signJwkResource) { this.signKeys = JwkKeys.builder().resource(signJwkResource).build(); return this; @@ -764,6 +779,7 @@ public Builder signJwk(Resource signJwkResource) { * @param verifyJwkResource resource pointing to a JSON with keys * @return updated builder instance */ + @ConfiguredOption(key = "atn-token.jwk.resource") public Builder verifyJwk(Resource verifyJwkResource) { this.verifyKeys = JwkKeys.builder().resource(verifyJwkResource).build(); @@ -776,6 +792,7 @@ public Builder verifyJwk(Resource verifyJwkResource) { * @param issuer issuer to add to the issuer claim * @return updated builder instance */ + @ConfiguredOption(key = "sign-token.jwt-issuer") public Builder issuer(String issuer) { this.issuer = issuer; return this; @@ -810,6 +827,7 @@ public Builder config(Config config) { * * @param audience audience string */ + @ConfiguredOption(key = "atn-token.jwt-audience") public void expectedAudience(String audience) { this.expectedAudience = audience; } @@ -821,6 +839,7 @@ public void expectedAudience(String audience) { * @param useJwtGroups whether to use {@code groups} claim from JWT to retrieve roles * @return updated builder instance */ + @ConfiguredOption("true") public Builder useJwtGroups(boolean useJwtGroups) { this.useJwtGroups = useJwtGroups; return this; diff --git a/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProviderService.java b/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProviderService.java index 39fec8ecf01..65434d793d8 100644 --- a/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProviderService.java +++ b/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProviderService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,9 +25,12 @@ * with {@link io.helidon.security.Security}. */ public class JwtProviderService implements SecurityProviderService { + + static final String PROVIDER_CONFIG_KEY = "jwt"; + @Override public String providerConfigKey() { - return "jwt"; + return PROVIDER_CONFIG_KEY; } @Override diff --git a/security/providers/jwt/src/main/java/module-info.java b/security/providers/jwt/src/main/java/module-info.java index 2751c3a59fa..25e68fc2ff1 100644 --- a/security/providers/jwt/src/main/java/module-info.java +++ b/security/providers/jwt/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -26,6 +26,8 @@ requires transitive io.helidon.security.util; requires java.logging; + requires static io.helidon.config.metadata; + exports io.helidon.security.providers.jwt; provides io.helidon.security.spi.SecurityProviderService with io.helidon.security.providers.jwt.JwtProviderService; diff --git a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/OidcConfig.java b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/OidcConfig.java index ca308415f2d..f2caf5a6081 100644 --- a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/OidcConfig.java +++ b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/OidcConfig.java @@ -1383,6 +1383,7 @@ public Builder cookieEncryptionEnabledIdToken(boolean cookieEncryptionEnabled) { * @param crossOriginConfig cross-origin settings to apply to the redirect endpoint * @return updated builder instance */ + @ConfiguredOption(key = "cors") public Builder crossOriginConfig(CrossOriginConfig crossOriginConfig) { this.crossOriginConfig = crossOriginConfig; return this; From 0a73810a8d3ac80873144425beb2eb1bf81c2e3b Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Mon, 18 Jul 2022 09:46:11 -0700 Subject: [PATCH 35/51] Integrate build tools 3.0.0-RC2 (#4562) * Integrate Build Tools v3.0.0-RC2 --- applications/pom.xml | 4 ++-- pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/pom.xml b/applications/pom.xml index 1e118d23bca..fe94fc51a28 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -47,8 +47,8 @@ 3.1.2 1.6.0 3.0.0-M5 - 3.0.0-RC1 - 3.0.0-RC1 + 3.0.0-RC2 + 3.0.0-RC2 3.0.2 1.5.0.Final 0.5.1 diff --git a/pom.xml b/pom.xml index 6917f656ab3..e93c5a898c6 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 1.6.0 3.0.0-M5 2.3 - 3.0.0-RC1 + 3.0.0-RC2 ${version.lib.hibernate} 0.8.5 1.1.0 @@ -123,7 +123,7 @@ 1.4 2.14.0 - 3.0.0-M3 + 3.0.0-RC2 https://jakarta.ee/specifications/restful-ws/3.0/apidocs/ From 7b8e8956ca22d13e8b76ed9cd11328f32818d7f5 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Mon, 18 Jul 2022 21:09:10 +0200 Subject: [PATCH 36/51] 3.0 archetypes minor issues : Multiple fix to templates (#4556) * Multiple fix to templates Signed-off-by: tvallin * readme sections for native image and jlink + enable tests Signed-off-by: tvallin * readme sections for native image and jlink left over for bare Signed-off-by: tvallin * readme sections for metrics left over Signed-off-by: tvallin * remove db mp filter and corresponding unit tests Signed-off-by: tvallin * fix docker.native-image unresolved properties Signed-off-by: tvallin --- archetypes/helidon/pom.xml | 3 - .../src/main/archetype/common/docker.xml | 56 ++++++++- .../archetype/common/files/README.md.mustache | 9 ++ .../main/archetype/common/media-sources.xml | 7 ++ .../main/archetype/common/observability.xml | 36 +++++- .../src/main/archetype/common/sources.xml | 7 ++ .../main/archetype/mp/custom/custom-mp.xml | 12 +- .../src/main/archetype/mp/custom/database.xml | 42 +++++++ .../main/archetype/mp/custom/files/README.md | 4 +- .../mp/custom/files/README.native.md | 20 +++ .../__pkg__/SimpleGreetResource.java.mustache | 55 +++++++++ .../archetype/mp/database/database-mp.xml | 7 +- .../archetype/mp/database/files/README.md | 68 +---------- .../mp/database/files/README.native.md | 39 ++++++ .../META-INF/microprofile-config.properties | 16 --- .../META-INF/persistence.xml.mustache | 39 ------ .../archetype/mp/quickstart/files/README.md | 2 - .../mp/quickstart/files/README.native.md | 22 ++++ .../archetype/mp/quickstart/quickstart-mp.xml | 13 +- .../main/archetype/se/custom/custom-se.xml | 15 ++- .../src/main/archetype/se/custom/database.xml | 115 +++++++++++------- .../main/archetype/se/custom/files/README.md | 4 +- .../se/custom/files/README.native.md | 20 +++ .../SimpleGreetService.java.json.mustache | 2 +- .../SimpleGreetService.java.jsonp.mustache | 2 +- .../__pkg__/SimpleGreetService.java.mustache | 69 +++++++++++ .../archetype/se/database/database-se.xml | 7 +- .../archetype/se/database/files/README.md | 53 -------- .../se/database/files/README.native.md | 37 ++++++ .../archetype/se/quickstart/files/README.md | 2 - .../se/quickstart/files/README.native.md | 22 ++++ .../archetype/se/quickstart/quickstart-se.xml | 6 + 32 files changed, 555 insertions(+), 256 deletions(-) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/README.native.md create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/SimpleGreetResource.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/README.native.md delete mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/microprofile-config.properties delete mode 100644 archetypes/helidon/src/main/archetype/mp/database/files/src/test/resources/META-INF/persistence.xml.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/quickstart/files/README.native.md create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/README.native.md create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/se/database/files/README.native.md create mode 100644 archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md diff --git a/archetypes/helidon/pom.xml b/archetypes/helidon/pom.xml index 1c5f236ae64..93408973f3f 100644 --- a/archetypes/helidon/pom.xml +++ b/archetypes/helidon/pom.xml @@ -48,9 +48,6 @@ helidon-archetype-maven-plugin false - - true - ${project.version} diff --git a/archetypes/helidon/src/main/archetype/common/docker.xml b/archetypes/helidon/src/main/archetype/common/docker.xml index 3481293d1d5..44a0b148d59 100644 --- a/archetypes/helidon/src/main/archetype/common/docker.xml +++ b/archetypes/helidon/src/main/archetype/common/docker.xml @@ -53,13 +53,59 @@ - + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/common/files/README.md.mustache b/archetypes/helidon/src/main/archetype/common/files/README.md.mustache index 53589e8af9a..938601a61e0 100644 --- a/archetypes/helidon/src/main/archetype/common/files/README.md.mustache +++ b/archetypes/helidon/src/main/archetype/common/files/README.md.mustache @@ -4,12 +4,21 @@ ## Build and run +{{#run-comment}} +{{.}} +{{/run-comment}} + With JDK11+ ```bash mvn package java -jar target/{{artifactId}}.jar ``` +## Exercise the application +{{#readme-exercise-the-application}} +{{.}} +{{/readme-exercise-the-application}} + {{#readme-sections}} {{.}} {{/readme-sections}} diff --git a/archetypes/helidon/src/main/archetype/common/media-sources.xml b/archetypes/helidon/src/main/archetype/common/media-sources.xml index 85a5b1a9e41..2cc5a3c2e77 100644 --- a/archetypes/helidon/src/main/archetype/common/media-sources.xml +++ b/archetypes/helidon/src/main/archetype/common/media-sources.xml @@ -22,6 +22,13 @@ + + files + + src/*/java/**/SimpleGreetService.java.mustache + src/*/java/**/SimpleGreetResource.java.mustache + + files diff --git a/archetypes/helidon/src/main/archetype/common/observability.xml b/archetypes/helidon/src/main/archetype/common/observability.xml index 26f7bc0d36a..c1803e26907 100644 --- a/archetypes/helidon/src/main/archetype/common/observability.xml +++ b/archetypes/helidon/src/main/archetype/common/observability.xml @@ -37,6 +37,24 @@ description="Expose metrics using the MicroProfile API"> + + + org.eclipse.microprofile.metrics @@ -51,6 +69,12 @@ helidon-metrics + + io.helidon.metrics.MetricsSupport + + + + org.eclipse.microprofile.metrics.MetricUnits org.eclipse.microprofile.metrics.annotation.Counted @@ -303,10 +327,16 @@ allRequests_total 0.0 optional="true"> - + + + io.helidon.metrics + helidon-metrics + + + io.helidon.metrics.MetricsSupport - + @@ -329,7 +359,7 @@ allRequests_total 0.0 assertThat(response.status().code(), is(200)); }]]> - + src/*/java/**/*.java.mustache + + src/test/**/*.mustache + files src/*/resources/**/*.mustache + + src/test/resources/**/*.mustache + files @@ -41,6 +47,7 @@ **/*.mustache + src/test/resources/** diff --git a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml index af6ce002489..0286e476465 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml @@ -21,15 +21,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://helidon.io/archetype/2.0 https://helidon.io/xsd/archetype-2.0.xsd"> - - - + + files @@ -39,9 +38,14 @@ Minimal Helidon MP project suitable to start from scratch. - + + + + diff --git a/archetypes/helidon/src/main/archetype/mp/custom/database.xml b/archetypes/helidon/src/main/archetype/mp/custom/database.xml index 73f6c93878d..e9051daf8b4 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/database.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/database.xml @@ -171,6 +171,17 @@ @@ -270,6 +290,9 @@ docker run --rm --name mongo -p 27017:27017 mongo + + + io.helidon.dbclient diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/README.md b/archetypes/helidon/src/main/archetype/se/custom/files/README.md index 80320bc473f..1e961480003 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/README.md +++ b/archetypes/helidon/src/main/archetype/se/custom/files/README.md @@ -1,6 +1,4 @@ -## Exercise the application - ``` -curl -X GET http://localhost:8080/greet +curl -X GET http://localhost:8080/simple-greet {"message":"Hello World!"} ``` diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/README.native.md b/archetypes/helidon/src/main/archetype/se/custom/files/README.native.md new file mode 100644 index 00000000000..608377e3e72 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/README.native.md @@ -0,0 +1,20 @@ +Make sure you have GraalVM locally installed: + +``` +$GRAALVM_HOME/bin/native-image --version +``` + +Build the native image using the native image profile: + +``` +mvn package -Pnative-image +``` + +This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a while to complete. +Once it completes start the application using the native executable (no JVM!): + +``` +./target/{{artifactId}} +``` + +Yep, it starts fast. You can exercise the application’s endpoints as before. \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache index 977277ee2cf..d520b4706bb 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.json.mustache @@ -15,7 +15,7 @@ import io.helidon.webserver.Service; * A simple service to greet you. Examples: * * Get default greeting message: - * curl -X GET http://localhost:8080/greet + * curl -X GET http://localhost:8080/simple-greet * * The message is returned as a JSON object */ diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache index 1337d62c2d3..6d0679c1992 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.jsonp.mustache @@ -20,7 +20,7 @@ import io.helidon.webserver.Service; * A simple service to greet you. Examples: * * Get default greeting message: - * curl -X GET http://localhost:8080/greet + * curl -X GET http://localhost:8080/simple-greet * * The message is returned as a JSON object */ diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.mustache new file mode 100644 index 00000000000..b9e053b37c5 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SimpleGreetService.java.mustache @@ -0,0 +1,69 @@ +package {{package}}; + +{{#SimpleGreetService-imports}} +import {{.}}; +{{/SimpleGreetService-imports}} + +import io.helidon.config.Config; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +import java.util.logging.Logger; + +/** + * A simple service to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/simple-greet + * + * The message is returned as a JSON object + */ +public class SimpleGreetService implements Service { + + private static final Logger LOGGER = Logger.getLogger(SimpleGreetService.class.getName()); +{{#SimpleGreetResource-static-fields}} +{{.}} +{{/SimpleGreetResource-static-fields}} + + private final String greeting; + + SimpleGreetService(Config config) { + greeting = config.get("app.greeting").asString().orElse("Ciao"); + } + +{{#SimpleGreetResource-constructor}} +{{.}} +{{/SimpleGreetResource-constructor}} + + /** + * A service registers itself by updating the routing rules. + * + * @param rules the routing rules. + */ + @Override + public void update(Routing.Rules rules) { + rules.get("/", this::getDefaultMessageHandler); +{{#SimpleGreetResource-update}} +{{.}} +{{/SimpleGreetResource-update}} + } + + /** + * Return a worldly greeting message. + * + * @param request the server request + * @param response the server response + */ + private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { + String msg = String.format("%s %s!", greeting, "World"); + LOGGER.info("Greeting message is " + msg); + response.send(msg); + } + +{{#SimpleGreetService-methods}} +{{.}} +{{/SimpleGreetService-methods}} + +} diff --git a/archetypes/helidon/src/main/archetype/se/database/database-se.xml b/archetypes/helidon/src/main/archetype/se/database/database-se.xml index 5fde77d389b..021eb0cffd5 100644 --- a/archetypes/helidon/src/main/archetype/se/database/database-se.xml +++ b/archetypes/helidon/src/main/archetype/se/database/database-se.xml @@ -28,9 +28,14 @@ Helidon SE Database - + + + + diff --git a/archetypes/helidon/src/main/archetype/se/database/files/README.md b/archetypes/helidon/src/main/archetype/se/database/files/README.md index ebdae2f9b62..5bd9be416ba 100644 --- a/archetypes/helidon/src/main/archetype/se/database/files/README.md +++ b/archetypes/helidon/src/main/archetype/se/database/files/README.md @@ -1,13 +1,3 @@ -## Build and run - -With JDK11+ -```bash -mvn package -java -jar target/{{artifactId}}.jar -``` - -## Exercise the application - ``` curl -X GET http://localhost:8080/pokemon [{"id":1,"idType":12,"name":"Bulbasaur"}, ...] @@ -18,46 +8,3 @@ curl -X GET http://localhost:8080/type curl -H "Content-Type: application/json" --request POST --data '{"id":100, "idType":1, "name":"Test"}' http://localhost:8080/pokemon ``` -## GraalVM Native Support - -The generation of native binaries requires an installation of GraalVM 20.1.0+. For more -information about the steps necessary to use GraalVM with Helidon -see https://helidon.io/docs/v2/#/se/guides/36_graalnative. - -The H2 Database when configured to use the in-memory mode is currently _not compatible_ -with GraalVM native. -In order to produce a native binary, you must run the H2 Database as a separate process -and use a network connection for access. The simplest way to do this is by starting a Docker -container as follows: - -``` -docker run -d -p 1521:1521 -p 81:81 -e H2_OPTIONS='-ifNotExists' --name=h2 oscarfonts/h2 -``` - -The resulting container will listen to port 1521 for network connections. -Switch the `url` in `application.yaml` to use a TCP connection: - -``` -url: jdbc:h2:tcp://localhost:1521/test -``` - -Next, uncomment the following dependency in your project's pom file: - -``` - - io.helidon.integrations.db - h2 - -``` - -With all these changes, re-build your project and verify that all tests are passing. -Finally, you can build a native binary using Maven as follows: - -``` -mvn -Pnative-image install -DskipTests -``` - -The generation of the executable binary may take a few minutes to complete depending on -your hardware and operating system. When completed, the executable file will be available -under the `target` directory and be named after the artifact ID you have chosen during the -project generation phase. diff --git a/archetypes/helidon/src/main/archetype/se/database/files/README.native.md b/archetypes/helidon/src/main/archetype/se/database/files/README.native.md new file mode 100644 index 00000000000..aed0dd6ceb4 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/database/files/README.native.md @@ -0,0 +1,37 @@ +The generation of native binaries requires an installation of GraalVM 22.1.0+. + +In order to produce a native binary, you must run the {{db}} Database as a separate process +and use a network connection for access. The simplest way to do this is by starting a Docker +container as follows: + +``` +{{readme-native-docker}} +``` + +The resulting container will listen to port 1521 for network connections. +Switch the `url` in `application.yaml` : + +``` +{{readme-native-url}} +``` + +Next, uncomment the following dependency in your project's pom file: + +``` + + io.helidon.integrations.db + {{integration-artifactId}} + +``` + +With all these changes, re-build your project and verify that all tests are passing. +Finally, you can build a native binary using Maven as follows: + +``` +mvn -Pnative-image install -DskipTests +``` + +The generation of the executable binary may take a few minutes to complete depending on +your hardware and operating system. When completed, the executable file will be available +under the `target` directory and be named after the artifact ID you have chosen during the +project generation phase. diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/files/README.md b/archetypes/helidon/src/main/archetype/se/quickstart/files/README.md index cec80e8673f..2778039fc2b 100644 --- a/archetypes/helidon/src/main/archetype/se/quickstart/files/README.md +++ b/archetypes/helidon/src/main/archetype/se/quickstart/files/README.md @@ -1,5 +1,3 @@ -## Exercise the application - ``` curl -X GET http://localhost:8080/greet {"message":"Hello World!"} diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md b/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md new file mode 100644 index 00000000000..8501b9435dc --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md @@ -0,0 +1,22 @@ +## Building a Native Image + +Make sure you have GraalVM locally installed: + +``` +$GRAALVM_HOME/bin/native-image --version +``` + +Build the native image using the native image profile: + +``` +mvn package -Pnative-image +``` + +This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a while to complete. +Once it completes start the application using the native executable (no JVM!): + +``` +./target/{{artifactId}} +``` + +Yep, it starts fast. You can exercise the application’s endpoints as before. \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml index d72c4ffaee7..b115f82fecd 100644 --- a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml +++ b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml @@ -30,6 +30,12 @@ Sample Helidon SE project that includes multiple REST operations. + + + + + + From a44320d2b86d2291dfadd6097877653f8087f043 Mon Sep 17 00:00:00 2001 From: Arjav Desai Date: Mon, 18 Jul 2022 16:00:05 -0500 Subject: [PATCH 37/51] [New Docs PR] Config Docs (#4477) --- config/config-mp/pom.xml | 12 + .../io/helidon/config/mp/MpConfigBuilder.java | 4 + .../config-mp/src/main/java/module-info.java | 1 + docs/config/config_reference.adoc | 1 + .../io_helidon_config_mp_MpConfigBuilder.adoc | 55 ++++ ...urces.adoc => advanced-configuration.adoc} | 224 ++++++++++------ docs/mp/config/introduction.adoc | 132 ++++++--- docs/mp/guides/config.adoc | 26 +- docs/se/config/advanced-configuration.adoc | 155 +++-------- docs/se/config/config-profiles.adoc | 12 +- docs/se/config/config-sources.adoc | 103 ------- docs/se/config/extensions.adoc | 28 +- docs/se/config/hierarchical-features.adoc | 38 ++- docs/se/config/introduction.adoc | 251 ++++++++++++------ docs/se/config/mutability-support.adoc | 19 +- docs/se/config/property-mapping.adoc | 18 +- docs/se/config/supported-formats.adoc | 19 +- docs/se/guides/config.adoc | 4 +- docs/sitegen.yaml | 7 +- 19 files changed, 635 insertions(+), 474 deletions(-) create mode 100644 docs/config/io_helidon_config_mp_MpConfigBuilder.adoc rename docs/mp/config/{config-sources.adoc => advanced-configuration.adoc} (63%) delete mode 100644 docs/se/config/config-sources.adoc diff --git a/config/config-mp/pom.xml b/config/config-mp/pom.xml index b3ff149b199..a32f685c4e4 100644 --- a/config/config-mp/pom.xml +++ b/config/config-mp/pom.xml @@ -54,6 +54,18 @@ org.eclipse.microprofile.config microprofile-config-api + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + org.junit.jupiter junit-jupiter-api diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java index 249e4c309a2..06a195b0627 100644 --- a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java @@ -63,6 +63,8 @@ import io.helidon.config.ConfigException; import io.helidon.config.ConfigMappers; import io.helidon.config.ConfigValue; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.config.mp.spi.MpConfigFilter; import io.helidon.config.mp.spi.MpMetaConfigProvider; @@ -77,6 +79,7 @@ /** * Configuration builder. */ +@Configured(prefix = "mp.config") class MpConfigBuilder implements ConfigBuilder { private static final Logger LOGGER = Logger.getLogger(MpConfigBuilder.class.getName()); private static final String DEFAULT_CONFIG_SOURCE = "META-INF/microprofile-config.properties"; @@ -363,6 +366,7 @@ public Config build() { * @param profile name of the profile, such as {@code dev, test} * @return updated builder instance */ + @ConfiguredOption(key = "profile", type = String.class, description = "Configure an explicit profile name.") public MpConfigBuilder profile(String profile) { this.profile = profile; return this; diff --git a/config/config-mp/src/main/java/module-info.java b/config/config-mp/src/main/java/module-info.java index 8be60aac0b6..6bc7bc51b4d 100644 --- a/config/config-mp/src/main/java/module-info.java +++ b/config/config-mp/src/main/java/module-info.java @@ -24,6 +24,7 @@ requires transitive microprofile.config.api; requires jakarta.annotation; requires io.helidon.common.serviceloader; + requires static io.helidon.config.metadata; exports io.helidon.config.mp; exports io.helidon.config.mp.spi; diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index 6adca3245e1..02c45cdf6ea 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -56,6 +56,7 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_metrics_api_MetricsSettings.adoc[MetricsSettings (metrics.api)] - xref:{rootdir}/config/io_helidon_metrics_serviceapi_MetricsSupport.adoc[MetricsSupport (metrics.serviceapi)] - xref:{rootdir}/config/io_helidon_integrations_micrometer_MicrometerSupport.adoc[MicrometerSupport (integrations.micrometer)] +- xref:{rootdir}/config/io_helidon_config_mp_MpConfigBuilder.adoc[MpConfigBuilder (config.mp)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig (security.providers.oidc.common)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[OidcProvider (security.providers.oidc)] - xref:{rootdir}/config/io_helidon_openapi_OpenAPISupport.adoc[OpenAPISupport (openapi)] diff --git a/docs/config/io_helidon_config_mp_MpConfigBuilder.adoc b/docs/config/io_helidon_config_mp_MpConfigBuilder.adoc new file mode 100644 index 00000000000..2351f683356 --- /dev/null +++ b/docs/config/io_helidon_config_mp_MpConfigBuilder.adoc @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.config.mp.MpConfigBuilder +:keywords: helidon, config, io.helidon.config.mp.MpConfigBuilder +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.config.mp.MpConfigBuilder +include::{rootdir}/includes/attributes.adoc[] + += MpConfigBuilder (config.mp) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.config.mp/io/helidon/config/mp/MpConfigBuilder.html[io.helidon.config.mp.MpConfigBuilder] + + +[source,text] +.Config key +---- +mp.config +---- + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`profile` |string |{nbsp} |Configure an explicit profile name. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/mp/config/config-sources.adoc b/docs/mp/config/advanced-configuration.adoc similarity index 63% rename from docs/mp/config/config-sources.adoc rename to docs/mp/config/advanced-configuration.adoc index 02a8060f14e..c1b6049cf6f 100644 --- a/docs/mp/config/config-sources.adoc +++ b/docs/mp/config/advanced-configuration.adoc @@ -19,55 +19,77 @@ = Microprofile Config Sources :description: MicroProfile Config Sources :keywords: helidon, mp, ordinal, mpconfig, yamlmpconfig +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] -A Config Source provides configuration values from different sources such as property files and user classes that are registered by the application. +== ToC -Helidon configuration sources can use different formats for the configuration data. You can specify the format on a per-source bases, mixing and matching formats as required. +- <> +- <> +- <> +- <> +- <> +- <> -The following configuration sources can be used to retrieve the configuration: +== Creating MicroProfile Config Sources for Manual Setup of Config + +You can use the following methods to create MicroProfile Config Sources to manually set up the Config from `org.eclipse.microprofile.config.spi.ConfigProviderResolver#getBuilder()` on `io.helidon.config.mp.MpConfigSources` class: [cols="3,5"] |=== -|Source |Description - -|System properties |A mutable source that uses `System.getProperties()` to obtain configuration values. - -|Environment variables |An immutable source that uses `System.env()` to obtain configuration values and resolves aliases as defined by the MicroProfile Config specification. +|Method |Description -|`META-INF/microprofile-config.properties` |The properties config source as defined by MicroProfile Config specification. +|`systemProperties()` |System properties config source. -|File |Creates the source from a properties file on the file system with `MpConfigSources.create(Path)`. +|`environmentVariables()` |Environment variables config source. -|URL |Creates the source from properties from an URL with `MpConfigSources.create(URL)`. +|`create(java.nio.file.Path)` |Loads a properties file from file system. + +To load the properties file from file system with custom name, use `create(String, java.nio.file.Path)`. -|`Map` |Creates the source from a Map with `MpConfigSources.create(Map)`. +|`create(java.util.Map)` |Creates an in-memory source from map. + +To create an in-memory source from map with custom name, use `create(String, java.util.Map)`. -|`Properties` |Creates the source directly from Properties with `MpConfigSources.create(Properties)`. +|`create(java.util.Properties)` |Creates an in-memory source from properties. + +To create an in-memory source from properties with custom name, use `create(String, java.util.Properties)`. -|File on classpath |Creates the source from a properties file on classpath with `MpConfigSources.classpath(String)`. +|=== -|YAML |Creates the source from YAML using `YamlMpConfigSource.create(Path)` or `YamlMpConfigSource.create(URL)`. +=== Create Custom Map MicroProfile Config Source +You can create Microprofile Config Source from a map. -|=== +[source,java] +.Create MicroProfile Config Source based on Environment Variables and Custom Map +---- +ConfigProviderResolver resolver = ConfigProviderResolver.instance(); -== Understanding the Ordering of Default Config Sources +org.eclipse.microprofile.config.Config config = resolver.getBuilder() <1> + .withSources(MpConfigSources.environmentVariables()) <2> + .withSources(MpConfigSources.create(Map.of("key","value"))) <3> + .build(); <4> -The default MicroProfile Config Sources are: +resolver.registerConfig(config, null); <5> +---- +<1> Creates MicroProfile Config Source builder. +<2> Adds environment variables. +<3> Adds a custom map. +<4> Builds the MicroProfile Config Source. +<5> Registers the config, so it can be used by other components -* System properties (ordinal=400) -* Environment variables (ordinal=300) -* /META-INF/microprofile-config.properties (ordinal=100) +=== Create YAML MicroProfile Config Source -Each Config Source has an ordinal that determines the priority of the Config Source. -A Config Source with higher ordinal has higher priority as compared to the Config Source with -lower ordinal. The values taken from the high-priority Config Source overrides the values -from low-priority Config Source. +You can create YAML Microprofile Config Source from a path or a URL. When you create a MicroProfile instance from the builder, +the `YamlMpConfigSource` allows you to create a custom Config Source and register +it with the builder. -This helps to customize the configuration of Config Sources using external Config Source -if an external Config Source has higher ordinal values than the built-in Config Sources of the application. +[source,java] +.Create YamlMPConfigSource from a path +---- +ConfigProviderResolver.instance().newBuilder() + .withSources(YamlMpConfigSource.create(path)) + .build(); +---- == Creating Custom Config Sources @@ -121,65 +143,6 @@ public class CustomConfigSource implements ConfigSource { <3> Returns the value of the requested key, or `null` if the key is not available <4> Returns the ordinal of this Config Source. - -== Creating MicroProfile Config Sources for Manual Setup of Config - -You can use the following methods to create MicroProfile Config Sources to manually set up the Config from `org.eclipse.microprofile.config.spi.ConfigProviderResolver#getBuilder()` on `io.helidon.config.mp.MpConfigSources` class: - -[cols="3,5"] -|=== -|Method |Description - -|`systemProperties()` |System properties config source. - -|`environmentVariables()` |Environment variables config source. - -|`create(java.nio.file.Path)` |Loads a properties file from file system. + -To load the properties file from file system with custom name, use `create(String, java.nio.file.Path)`. - -|`create(java.util.Map)` |Creates an in-memory source from map. + -To create an in-memory source from map with custom name, use `create(String, java.util.Map)`. - -|`create(java.util.Properties)` |Creates an in-memory source from properties. + -To create an in-memory source from properties with custom name, use `create(String, java.util.Properties)`. - -|=== - -=== Create Custom Map MicroProfile Config Source -You can create Microprofile Config Source from a map. - -[source,java] -.Create MicroProfile Config Source based on Environment Variables and Custom Map ----- -ConfigProviderResolver resolver = ConfigProviderResolver.instance(); - -org.eclipse.microprofile.config.Config config = resolver.getBuilder() <1> - .withSources(MpConfigSources.environmentVariables()) <2> - .withSources(MpConfigSources.create(Map.of("key","value"))) <3> - .build(); <4> - -resolver.registerConfig(config, null); <5> ----- -<1> Creates MicroProfile Config Source builder. -<2> Adds environment variables. -<3> Adds a custom map. -<4> Builds the MicroProfile Config Source. -<5> Registers the config, so it can be used by other components - -=== Create Yaml MicroProfile Config Source - -You can create Yaml Microprofile Config Source from a path or a URL. When you create a MicroProfile instance from the builder, -the `YamlMpConfigSource` allows you to create a custom Config Source and register -it with the builder. - -[source,java] -.Create YamlMPConfigSource from a path ----- -ConfigProviderResolver.instance().newBuilder() - .withSources(YamlMpConfigSource.create(path)) - .build(); ----- - == Creating MicroProfile Config Sources from meta-config Instead of directly specifying the configuration sources in your code, you can use meta-configuration in a file that declares @@ -209,6 +172,10 @@ sources: optional: true <9> - type: "yaml" <10> classpath: "META-INF/database.yaml" <11> + - type: "hocon" <12> + classpath: "custom-application.conf" <13> + - type: "json" <14> + path: "path: conf/custom-application.json" <15> ---- @@ -223,6 +190,91 @@ sources: <9> The file is optional (if not optional and no file is found, the bootstrap fails) <10> Loads a YAML file <11> Location of the file: `META-INF/database.yaml` on the classpath +<12> Loads a HOCON file +<13> Location of the file: `custom-application.conf` on the classpath +<14> Loads a JSON file +<15> Location of the file: `conf/custom-application.json` relative to the directory of where the app was executed on the file system. + +*Important Note:* To enable support for `HOCON` and `JSON` types, add the following dependency to your project’s pom.xml. + +[source,xml] +---- + + io.helidon.config + helidon-config-hocon-mp + +---- + +== Extending Meta-Config to Create a Custom Config Source Type + +Helidon meta-config by default supports the following types: environment-variables, system-properties, properties, yaml, hocon and json. Users can also extend meta-config to create a custom config source type by loading it using the Java Service Loader pattern. This is achieved by implementing `io.helidon.config.mp.spi.MpMetaConfigProvider` SPI and registering it as a service (Using `META-INF/services/${class-name}` file when using classpath, or using the `provides` statement in `module-info.java` when using module path). + +The interface `io.helidon.config.mp.spi.MpMetaConfigProvider` requires implementation of the following methods: + +* `Set supportedTypes()` +* `List create(String type, Config metaConfig, String profile);` + +=== Example of a Meta-Config Custom Type + +[source,java] +---- +public class CustomMpMetaConfigProvider implements MpMetaConfigProvider { + @Override + public Set supportedTypes() { + return Set.of("custom"); <1> + } + + @Override + public List create(String type, Config metaConfig, String profile) { + ConfigValue pathConfig = metaConfig.get("path").as(Path.class); + if (pathConfig.isPresent()) { <2> + Path path = pathConfig.get(); + List sources = sourceFromPath(path, profile); <3> + if (sources != null && !sources.isEmpty()) { + return result; + } + location = "path " + path.toAbsolutePath(); + } else { + ConfigValue classpathConfig = metaConfig.get("classpath").as(String.class); + if (classpathConfig.isPresent()) { <4> + String classpath = classpathConfig.get(); + List sources = sourceFromClasspath(classpath, profile); <5> + if (sources != null && !sources.isEmpty()) { + return sources; + } + location = "classpath " + classpath; + } else { + ConfigValue urlConfig = metaConfig.get("url").as(URL.class); + if (urlConfig.isPresent()) { <6> + URL url = urlConfig.get(); + List sources = sourceFromUrlMeta(url, profile); <7> + if (sources != null && !sources.isEmpty()) { + return sources; + } + location = "url " + url; + } else { + throw new ConfigException("No config source location for " + config.key()); + } + } + } + } + if (metaConfig.get("optional").asBoolean().orElse(false);) { + return List.of(); <8> + } + throw new ConfigException("Meta configuration could not find non-optional config source on " + location); <9> + +} +---- + +<1> Returns the names of the types that will be supported in this meta-config. +<2> Processes config source from file system if `path` is provided. +<3> Method to parse config source from a specified `path` +<4> Processes config source from classpath location if `classpath` is provided. +<5> Method to parse config source from a specified `classpath` +<6> Processes config source from URL location if `location` is provided. +<7> Method to parse config source from a specified `url` +<8> Returns an empty result if set to `optional` and config source is not found. +<9> Throws a ConfigException if not set to `optional` and config source is not found. == Creating MicroProfile Config Source from Helidon SE Config Source diff --git a/docs/mp/config/introduction.adoc b/docs/mp/config/introduction.adoc index 9f31bac0506..784986471da 100644 --- a/docs/mp/config/introduction.adoc +++ b/docs/mp/config/introduction.adoc @@ -18,18 +18,28 @@ /////////////////////////////////////////////////////////////////////////////// = MicroProfile Config -:toc: -:toc-placement: preamble :spec-name: MicroProfile Config -:description: MicroProfile Config support in Helidon MP +:description: {spec-name} support in Helidon MP :keywords: helidon, mp, microprofile, config, encryption, reference +:config-release: {version-lib-microprofile-config} :feature-name: MicroProfile Config :rootdir: {docdir}/../.. :microprofile-bundle: true include::{rootdir}/includes/mp.adoc[] -Helidon MicroProfile provides an implementation of Eclipse MicroProfile Config. It allows you to configure your applications using MicroProfile configuration sources and APIs. +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon {spec-name} is an implementation of https://github.com/eclipse/microprofile-config/[Eclipse {spec-name}]. You can configure your applications using MicroProfile's config configuration sources and APIs. You can also extend the configuration using MicroProfile SPI to add custom `ConfigSource` and `Converter`. include::{rootdir}/includes/dependencies.adoc[] @@ -41,31 +51,36 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== About {spec-name} - -Helidon MicroProfile Config is an implementation of https://github.com/eclipse/microprofile-config/[Eclipse MicroProfile Config]. -You can configure your applications using MicroProfile's config configuration sources and APIs. You can also extend the -configuration using MicroProfile SPI to add custom `ConfigSource` and `Converter`. +== Usage === {spec-name} Features -{spec-name} uses `ConfigSource` SPI to load configuration data, either from default configuration sources such -file `META-INF/microprofile-config.properties`, environment variables, and system properties; or from custom `ConfigSource` -located by Java Service Loader. +==== {spec-name} Sources + +A Config Source provides configuration values from different sources such as property files and user classes that are registered by the application. + +By default, the following configuration sources are used to retrieve the configuration: + +[cols="3,5"] +|=== +|Source |Description + +|System properties |A mutable source that uses `System.getProperties()` to obtain configuration values. + +|Environment variables |An immutable source that uses `System.env()` to obtain configuration values and resolves aliases as defined by the {spec-name} specification. -The data is then available through {spec-name} APIs to be injected into CDI Beans, or to be obtained using a `Config` -instance programmatically. +|`META-INF/microprofile-config.properties` |The properties config source as defined by {spec-name} specification. +|=== -{spec-name} provides typed access to configuration values, using built-in converters, and `Converter` implementations located -by Java Service Loader. +{spec-name} uses `ConfigSource` SPI to load configuration data, either from default configuration sources or from custom `ConfigSource` located by Java Service Loader. ==== Using {spec-name} API -You can use MicroProfile Config API to get configuration properties by using `ConfigProvider.getConfig()` -or injecting configuration values with `@ConfigProperty`. +You can use {spec-name} API to get configuration properties by using a `Config` +instance programmatically or injecting configuration values with `@ConfigProperty`. [source,java] -.Using ConfigProvider.getConfig() +.Using `Config` ---- org.eclipse.microprofile.config.Config config = ConfigProvider.getConfig(); config.getOptionalValue("app.greeting", String.class).orElse("Hello"); @@ -80,7 +95,23 @@ public GreetingProvider(@ConfigProperty(name = "app.greeting", defaultValue = "H } ---- -==== {spec-name} Config Sources +{spec-name} provides typed access to configuration values, using built-in converters, and `Converter` implementations located by Java Service Loader. + +==== Ordering of Default Config Sources + +In order to properly configure your application using configuration sources, you need to understand the precedence rules used to merge your configuration data. The default MicroProfile Config Sources ordering is: + +* System properties (ordinal=400) +* Environment variables (ordinal=300) +* /META-INF/microprofile-config.properties (ordinal=100) + +Each Config Source has an ordinal that determines the priority of the Config Source. +A Config Source with higher ordinal has higher priority as compared to the Config Source with +lower ordinal. The values taken from the high-priority Config Source overrides the values +from low-priority Config Source. + +This helps to customize the configuration of Config Sources using external Config Source +if an external Config Source has higher ordinal values than the built-in Config Sources of the application. The example below shows how the MicroProfile configuration file `microprofile-config.properties` can be used to modify the server listen port property. @@ -94,7 +125,7 @@ server.port=8080 server.host=0.0.0.0 ---- -==== {spec-name} Profiles +==== {spec-name} Profiles [[Config-Profiles]] {spec-name} supports a concept of configuration profiles. You can define a profile using the configuration property `mp.config.profile` (when using default configuration, this can be defined as a system property, environment variable or as a property in `microprofile-config.properties`). @@ -105,7 +136,31 @@ default properties. Profile properties can be defined using `%profile` prefix, s Helidon MicroProfile Config offers the following features on top of the specification: -* *References* + +=== Helidon {spec-name} Sources +Helidon configuration sources can use different formats for the configuration data. You can specify the format on a per source bases, mixing and matching formats as required. + +The following configuration sources can be used to retrieve the configuration: + +[cols="3,5"] +|=== +|Source |Description +|File |Creates the source from a properties file on the file system with `MpConfigSources.create(Path)`. + +|URL |Creates the source from properties from a URL with `MpConfigSources.create(URL)`. + +|`Map` |Creates the source from a Map with `MpConfigSources.create(Map)`. + +|`Properties` |Creates the source directly from Properties with `MpConfigSources.create(Properties)`. + +|File on classpath |Creates the source from a properties file on classpath with `MpConfigSources.classpath(String)`. + +|YAML |Creates the source from YAML using `YamlMpConfigSource.create(Path)` or `YamlMpConfigSource.create(URL)`. + +|=== + +See xref:advanced-configuration.adoc#_creating_microprofile_config_sources_for_manual_setup_of_config[manual setup of config] section for more information. + +==== References You can use `${reference}` to reference another configuration key in a key value. This allows to configure a single key to be reused in multiple other keys. @@ -117,7 +172,7 @@ service-1: "${uri}/service1" service-2: "${uri}/service2" ---- -* *Change support* + +==== Change support Polling (or change watching) for file based config sources (not classpath based). To enable polling for a config source created using meta configuration (see below), or using @@ -137,10 +192,10 @@ See link:{jdk-javadoc-url}/java.base/java/nio/file/WatchService.html |=== -* *Encryption* + +==== Encryption You can encrypt secrets using a master password and store them in a configuration file. The config encryption filter in MicroProfile Config is enabled by default. -For more information, see xref:../security/configuration-secrets.adoc[Configuration Secrets]. +For more information, see xref:{rootdir}/mp/security/configuration-secrets.adoc[Configuration Secrets]. [source,properties] .Example of encrypted secrets @@ -154,29 +209,40 @@ client_secret=${RSA=mYRkg+4Q4hua1kvpCCI2hg==} client_secret=${CLEAR=known_password} ---- -* *Meta Configuration* + +==== Meta Configuration You can configure the Config using Helidon MP Config meta configuration feature. The meta-config allows configuration of config sources and other configuration options, including addition of discovered sources and converters. -This is a Helidon specific feature available since version 2.3.0. See - xref:../config/config-sources.adoc[Microprofile Config Sources] for detailed information. +See + xref:advanced-configuration.adoc#_creating_microprofile_config_sources_from_meta_config[Microprofile Config Sources] for detailed information. NOTE: For backward compatibility, we will support usage of Helidon SE meta-configuration until version 3.0.0. Using this approach causes behavior that is not compatible with {spec-name} specification. +== Configuration + +Config sources can be configured using the following properties. + +The class responsible for configuration is: + +include::{rootdir}/config/io_helidon_config_mp_MpConfigBuilder.adoc[leveloffset=+1,tag=config] + +Current properties may be set in `application.yaml` or in `microprofile-config.properties` with `mp.config` prefix. + +See xref:#Config-Profiles[Config Profiles] for more information. + == Guides [PILLARS] ==== [CARD] .MP Config Guide -[link=../guides/config.adoc] +[xref={rootdir}/mp/guides/config.adoc] -- Step-by-step guide about using {spec-name} in your Helidon MP application. -- ==== -== Additional Information +== Reference -- link:{javadoc-base-url}/io.helidon.config/io/helidon/config/spi/package-summary.html[Helidon Config SPI] -- link:{javadoc-base-url}/io.helidon.config/io/helidon/config/package-summary.html[Helidon Config API] -- link:{microprofile-config-spec-url}[MicroProfile Config API] +* link:{microprofile-config-spec-url}}[{spec-name} Specifications] +* link:{microprofile-fault-tolerance-javadoc-url}[{spec-name} Javadocs] diff --git a/docs/mp/guides/config.adoc b/docs/mp/guides/config.adoc index b6e16cd3a91..846aac5708d 100644 --- a/docs/mp/guides/config.adoc +++ b/docs/mp/guides/config.adoc @@ -39,7 +39,7 @@ Helidon provides a very flexible and comprehensive configuration system, offerin You can include configuration data from a variety of sources using different formats, like JSON and YAML. Furthermore, you can customize the precedence of sources and make them optional or mandatory. This guide introduces Helidon MP configuration and demonstrates the fundamental concepts using several examples. -Refer to xref:../../se/config/introduction.adoc[Helidon Config] for the full configuration concepts documentation. +Refer to xref:{rootdir}/mp/config/introduction.adoc[Helidon Config] for the full configuration concepts documentation. === Create a Sample Helidon MP Project @@ -72,8 +72,8 @@ For example, if you specify a custom server port in `META-INF/microprofile-confi then your server will listen on that port. A main class is also required to start up the server and run the -application. By default the Quickstart sample project uses the built-in -Helidon main class. In this guide you want to use your own main class so you have +application. By default, the Quickstart sample project uses the built-in +Helidon main class. In this guide you want to use your own main class, so you have more control over the server initialization. First define your own `Main`: [source,java] @@ -148,7 +148,7 @@ NOTE: Because environment variable names are restricted to alphanumeric characte Helidon adds aliases to the environment configuration source, allowing entries with dotted and/or hyphenated keys to be overridden. For example, this mapping allows an environment variable named "APP_GREETING" to override an entry key named "app.greeting". In the same way, an environment variable named "APP_dash_GREETING" will map to -"app-greeting". See xref:../../se/config/advanced-configuration.adoc[Advanced Configuration] for more information. +"app-greeting". See link:{microprofile-config-spec-url}[Microprofile Config Specifications] for more information. The following examples will demonstrate the default precedence order. @@ -235,11 +235,8 @@ curl http://localhost:8080/greet == Accessing Config within an Application -You have used Helidon to customize configuration behavior from your code using the `Config` and -`Config.Builder` classes. The examples in this section will demonstrate how to access that config data -at runtime. As discussed previously, Helidon reads configuration from a config source, which uses a config parser -to translate the source into an immutable in-memory tree representing the configuration’s structure and values. -Your application uses the `Config` object to access the in-memory tree, retrieving config data. +The examples in this section will demonstrate how to access that config data +at runtime. Your application uses the `Config` object to access the in-memory tree, retrieving config data. The generated project already accesses configuration data in the `GreetingProvider` class as follows: @@ -372,10 +369,9 @@ curl http://localhost:8080/greet === Navigating the Config Tree Helidon offers a variety of methods to access in-memory configuration. These can be categorized as _key access_ or _tree navigation_. -You have been using _key access_ for all of the examples to this point. For example `app.greeting` is accessing -the `greeting` child node of the `app` parent node. There are many options for access this data using navigation -methods as described in xref:../../se/config/hierarchical-features.adoc[Hierarchical Configuration] and xref:../../se/config/advanced-configuration.adoc[Advanced Configuration]. -This simple example below demonstrates how to access a child node as a detached configuration sub-tree. +You have been using _key access_ for all the examples to this point. For example `app.greeting` is accessing the `greeting` child node of the `app` parent node. + +This simple example below demonstrates how to access a child node as a detached configuration subtree. [source,yaml] .Create a file `config-file.yaml` in the `helidon-quickstart-mp` directory and add the following contents: @@ -622,7 +618,9 @@ kubectl delete configmap helidon-configmap == Summary This guide has demonstrated how to use basic Helidon configuration features. For more information about using the advanced Helidon configuration features, including mutability support and extensions, see - xref:../../se/config/introduction.adoc[Helidon Configuration]. + xref:{rootdir}/mp/config/introduction.adoc[Helidon Configuration]. + +== References Refer to the following references for additional information: diff --git a/docs/se/config/advanced-configuration.adoc b/docs/se/config/advanced-configuration.adoc index 5b958a7b1c2..0211dc19705 100644 --- a/docs/se/config/advanced-configuration.adoc +++ b/docs/se/config/advanced-configuration.adoc @@ -17,17 +17,27 @@ /////////////////////////////////////////////////////////////////////////////// = Advanced Configuration Topics -:description: Helidon config advanced configuration +:description: Helidon advanced configuration :keywords: helidon, config, meta -:toc: preamble -:toclevels: 4 +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + This section discusses several advanced topics related to Helidon configuration. -== Advanced Config Sources and Config Parsers +== Advanced Config Sources === Environment Variables Config Source The config system supports using environment variables as a config source, and is @@ -116,7 +126,7 @@ assert secrets.get("password") // <4> <4> ...and the key `password` to `^ery$ecretP&ssword`. Remember that your application can process the contents of a given file -as configuration. See the xref:config-sources.adoc[config sources] section +as configuration. See the xref:introduction.adoc#config_sources[config sources] section and the link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html#file-java.lang.String-[`ConfigSources.file`] JavaDoc. @@ -282,15 +292,12 @@ Config config = Config.create( // <4> Specifies the merging strategy. This example uses the default fallback merging strategy. - -=== How Config Chooses Parsers [[Config-Advanced-Sources-SuitableParser]] -As the link:config-sources.adoc[config sources and parsers] section describes, -these two work together to read and translate configuration data from some +== Advanced Config Parsers +Config sources and parsers work together to read and translate configuration data from some external form into the corresponding in-memory config tree. -Although most applications are -explicit about the config sources they use in building a `Config`, the config system often -has to figure out what parser to use. It does so by: +=== How Config Chooses Parsers [[Config-Advanced-Sources-SuitableParser]] +Although most applications are explicit about the config sources they use in building a `Config`, the config system often has to figure out what parser to use. It does so by: 1. determining, the best that it can, the media type of the source, and 2. locating a parser that can translate that media type. @@ -301,15 +308,15 @@ has to figure out what parser to use. It does so by: Most applications let the config system try to infer the media type of the config source. -By default config source implementations use the +By default, config source implementations use the `io.helidon.common.media.type.MediaTypes` API to infer the source media type from the source, typically (but not always) based on the file type portion of the file path. Helidon media type module has a predefined set of mappings as configured in `common/media-type/src/main/resources/io/helidon/common/media/type/default-media-types.properties`, including the Config supported formats: `.properties`, `.yaml`, `.json` and `.conf`. To handle other formats you can implement and register your own `io.helidon.common.media.type.spi.MediaTypeDetector` Java Service -implementations. (Typically you would also write and register a config parser -to translate that format; see <> below.). +implementations. (Typically, you would also write and register a config parser +to translate that format; see <> below.) ===== By Application Directive Your application can specify what media type to use in interpreting a config @@ -384,7 +391,7 @@ from parsing that `String`. In this example, a YAML document contains a JSON document as a leaf. [source,yaml] -.YAML file with included JSON formated property +.YAML file with included JSON formatted property ---- secrets: username: "jose" @@ -434,7 +441,7 @@ which returns the appropriate media types (if any) for config keys. <2> The function says to treat the `app` property value as a JSON document and leave other nodes unchanged. <3> Other properties are loaded as expected. -<4> Property `app` is now an structured object node. +<4> Property `app` is now a structured object node. Because the function passed to `mediaTypeMapping` identifies the `app` node as a JSON document, the config system selects the config parser that is registered with the builder @@ -460,103 +467,21 @@ Config config = Config.create( <1> Uses the `parserMapping` method to map keys to parser instances. <2> Tells the config system to use the HOCON parser for translating the `String` -value of the `app` key. (HCON is a superset of JSON.) +value of the `app` key. (HOCON is a superset of JSON.) As before, the config system replaces the value node in the containing config tree with the config tree resulting from the additional parse. -=== Loading Config using Meta-configuration [[Config-Advanced-metaconfig]] - -This section is now available in xref:config-profiles.adoc[config profiles], - which make meta-configuration obsolete - you can achieve the same with less configuration. -A profile file is a meta-configuration file selected by the rules defined in the link above. - -Instead of including code in your application to construct config trees from -builders, sources, etc., you can instead prepare _meta-configuration_ in a file -that declares the sources to load and their attributes. - -You can either specify the meta-config file in your application or -allow the config system to search for and load meta-config from a preset -list of possible sources. - -==== Loading Config by Specifying a Meta-configuration File [[Config-Advanced-Sources-MetaSource]] -Your application loads -the configuration specified by a meta-config file by invoking the link:{config-javadoc-base-url}/io/helidon/config/MetaConfig.html#config-io.helidon.config.Config-[`MetaConfig.config(Config)`] -method, passing a config object read from the meta-config source as the argument; - -If you desire a `Config.Builder` instead of a fully built `Config` instance, -you can use the `Config.Builder.config(Config)` method to update the builder with -meta configuration. Your application can further fine-tune this builder -before using it to construct a `Config` tree. The config system interprets the meta-config -as directions for how to build a config tree, rather than as the config data itself. - -==== Loading Config from an Implicit Meta-configuration File [[Config-Advanced-Config-MetaConfig]] -The xref:introduction.adoc[introduction] section shows how to use -`Config.create()` to load config from one of several possible default config files. -That same method also searches for one of several possible default meta-config files -from which to load config sources to be used for the default config. - -The `Config.create()` method determines the default configuration from -the following search: - -. Attempt to load _meta-config_ from at most one of the following files, -first on the file system in current directory, then on classpath, checked in this order: -.. `meta-config.yaml` - meta configuration file in YAML format -.. `meta-config.conf` - meta configuration file in HOCON format -.. `meta-config.json` - meta configuration file in JSON format -.. `meta-config.properties` - meta configuration file in Java Properties format -. Otherwise, load _config_ from: -.. environment variables, and -.. Java system properties, and -.. at most one of the following, checking in this order: -... `application.yaml` - configuration file in YAML format -... `application.conf` - configuration file in HOCON format -... `application.json` - configuration file in JSON format -... `application.properties` - configuration file in Java Properties format - -Remember that the config system will check for these default meta-config and config files -only if the classpath includes the corresponding parsers. The introduction section on xref:introduction.adoc#built-in-formats[built-in formats] -section describes this further. - -See javadoc link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#config-io.helidon.config.Config-[`Config.Builder.config(Config)`]. - -==== Meta-configuration File Format -This section is now available in xref:config-profiles.adoc#Config-Profile-Format[config profile format] - -==== Loading Config using Meta-configuration -Here is how your application can use meta-configuration in a particular resource on the -classpath to load a `Config` tree: - -[source,java] -.Loading Config using Meta-configuration ----- -Config metaConfig = Config.create(classpath("config-meta-all.conf")); // <1> -Config config = MetaConfig.config(metaConfig); // <2> ----- -<1> We create a meta configuration from our meta configuration source -<2> This method populates the config sources from -all the actual sources declared in the meta-configuration. +== Config Keys with . in name -== Configuration Key As described in the xref:hierarchical-features.adoc#accessByKey[hierarchical features -section] each config node (except the root) has a non-null key. Here is the formal -definition of what keys can be: -[source,abnf] -.The ABNF syntax of config key ----- -config-key = *1( key-token *( "." key-token ) ) - key-token = *( unescaped / escaped ) - unescaped = %x00-2D / %x2F-7D / %x7F-10FFFF - ; %x2E ('.') and %x7E ('~') are excluded from 'unescaped' - escaped = "~" ( "0" / "1" ) - ; representing '~' and '.', respectively ----- +section] each config node (except the root) has a non-null key. [IMPORTANT] ========= To emphasize, the dot character ("`.`") has special meaning as a name separator in keys. To include a dot as a character in a key escape it as - "`~1`". To include a tilda escape it as "`~0`". +"`~1`". ========= For example, the following configuration file contains two object nodes with @@ -597,7 +522,7 @@ assert config.get(Key.escapeName("oracle.com")).name().equals("oracle.com"); // <1> Work with the first `oracle` object as usual. As always you can use the fully-qualified key `oracle.com` or chain `get(key)` - calls to access the `com` property value. +calls to access the `com` property value. <2> Config node `"oracle"` / `"com"` is a leaf node (has type `VALUE`)... <3> ... and has the name `com` (the last token in its key). <4> The second object has name `oracle.com`. The code must escape the @@ -607,7 +532,6 @@ that might be in the node's name, in this example in `oracle.com`. <6> The config node `"oracle.com"` has type `OBJECT`... <7> ...and name `"oracle.com"`. - == Filter, Overrides, and Token Substitution [[filters-and-overrides]] When your application retrieves a config value, the config system can transform it before returning the value, according to _filters_, _overrides_, and @@ -688,7 +612,7 @@ Config config = Config.builder() using the environment-specific override. == Executors for Asynchronous Config Activity -Various parts of the config system work asychronously: +Various parts of the config system work asynchronously: * polling strategies to detect changes to config sources, * publishers to notify your application when such changes occur, @@ -703,8 +627,8 @@ executors, but your application can specify different ones if necessary. The two methods `PollingStrategies.regular(Duration)` and `PollingStrategies.watch(Path)` return builders for their respective strategies. Both builders expose the `executor` method which your application can invoke, passing a -`java.util.concurrent.ScheduledExecutorService` instance it wants used for the -polling work. By default each polling strategy instance uses a separate thread +`java.util.concurrent.ScheduledExecutorService` instance it requires for the +polling work. By default, each polling strategy instance uses a separate thread pool executor. The following example shares the same executor for two different polling @@ -735,7 +659,7 @@ Config config = Config.create( === Publishers for Source Change Events Recall that when a polling strategy detects a change in a source, it informs -interested parties of the changes. By default each `Config.Builder` arranges +interested parties of the changes. By default, each `Config.Builder` arranges for the resulting `Config` tree to use a shared executor that reuses available threads from a pool, creating new threads as needed. The same executor is used for actually reloading the source. @@ -778,7 +702,7 @@ When your application supplies multiple sources to a config builder, as with `Config.create(Supplier...)` and `Config.create(List>)`, the config system automatically uses a _composite config source_ which aggregates the separate -sources but also listens for changes to any of the individual sources so it can +sources but also listens for changes to any of the individual sources, so it can delegate the change notification. For this change detection and notification the config system, by default, uses an executor with a dedicated thread pool that is shared across all `Config` instances. @@ -790,10 +714,10 @@ link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.CompositeBuilder. which extends `Config.Builder`. Because a composite source might yield more numerous change events -- because of the -multiple underlying sources -- your application can specify a _debounce timeout_ +multiple underlying sources -- your application can specify _debounce timeout_ for the composite source by invoking the `CompositeBuilder.changesDebounce(Duration)` -method. The composite source aggregates multiple change events within this period -into a single event and broadcasts that one instead and reloads the sources at +method. The composite source aggregates multiple change events within this _debounce timeout_ +period into a single event and broadcasts that one instead. Next, it reloads the sources at that time, not necessarily in response to every single change in any source. The default is `100` milliseconds. @@ -832,7 +756,7 @@ Your application can specify a non-default `Executor` for a tree to use for accepting and propagating those events by invoking the `changesExecutor` method on the `Config.Builder`. Each source subscriber has a dedicated buffer for holding changes events. This -defaults to 256 but you can tailor this value as needed. +defaults to 256, but you can tailor this value as needed. [source,java] .Customize config executor @@ -858,7 +782,7 @@ The `RetryPolicies.repeat(int retries)` method returns a link:{config-javadoc-base-url}/io/helidon/config/RetryPolicies.Builder.html[RetryPolicies.Builder]. Your application can invoke the retry policy builder's `executor` method to specify which `ScheduledExecutorService` instance it should use to schedule and -execute delayed retries. By default the config system uses a separate thread +execute delayed retries. By default, the config system uses a separate thread pool executor for each retry policy instance. [source,java] @@ -881,3 +805,4 @@ failing the initial load or preventing the source from being polled if so config <3> Uses the built-in _repeating_ implementation of `RetryPolicy` that can be used with any config source, but typically for ones that might suffer brief, intermittent outages. <4> Specifies the executor to use for loading and retries. + diff --git a/docs/se/config/config-profiles.adoc b/docs/se/config/config-profiles.adoc index 1db3eadbf5c..f37d545cb5e 100644 --- a/docs/se/config/config-profiles.adoc +++ b/docs/se/config/config-profiles.adoc @@ -19,12 +19,20 @@ = Configuration Profiles :description: Helidon config profiles :keywords: helidon, config, profile -:toc: preamble -:toclevels: 4 +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> + +== Overview + Configuration profiles provide a capability to prepare structure of configuration for each environment in advance, and then simply switch between these structures using a system property or an environment variable. diff --git a/docs/se/config/config-sources.adoc b/docs/se/config/config-sources.adoc deleted file mode 100644 index fd031b4c87f..00000000000 --- a/docs/se/config/config-sources.adoc +++ /dev/null @@ -1,103 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2018, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Loading Configuration: Config Sources and Parsers -:description: A summary of Helidon config sources and parsers -:keywords: Helidon, config, sources, parsers -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Configuration can be loaded from different types of locations and expressed in different -formats. This section describes how your application can use _config sources_ and -_config parsers_ together to load configuration data. - -== Overview -Each config source reads data from a location of a specific type, without regard -to the format of the config data there. Each config parser -converts data expressed in a particular format into the in-memory config data -structure that the rest of the config system uses, without any concern for where -that data resides or how it is physically retrieved. These two work together to prepare -data in a given format at a given location for the config system. -When your application prepares a `Config.Builder` it sets what ``ConfigSource``s and -``ConfigParser``s the builder should use in constructing the resulting `Config` object. - -== Config Sources -If your application uses the default configuration, then the config system -automatically sets up the config sources for you, as described in the -xref:introduction.adoc#introducing-the-config-system[config introduction]. - -If instead your application uses a link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html[`Config.Builder`], then it can invoke one of the `sources` methods on that builder to set which config sources it should use. - -The config system includes support for several types of config sources, for example: - -* a resource on the runtime classpath, -* environment variables, -* a file, -* Java system properties, -* a URL, -* a variety of in-memory data structures (`String`, `Map`, `Properties`) - -See the JavaDoc for the link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html[`ConfigSources`] -class for a complete list of the built-in config source types and how to use them. - -You can also extend the config system -to handle other types of sources by implementing the -link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigSource.html[`ConfigSource`] interface. See -the xref:extensions.adoc[extensions] documentation for complete information. - -See the xref:advanced-configuration.adoc[advanced topics] page for further -information on some more involved aspects of config sources. - -== Config Parsers [[parsers]] -When it reads configuration text from sources, the config system uses config parsers -to translate that text into the in-memory data structures representing that configuration. -The config system includes several built-in parsers, such as for the Java properties, YAML, JSON, and HOCON formats. See xref:introduction.adoc#built-in-formats[this section in the introduction] for -how to change your `pom.xml` to make parsers for those formats available to your -application. Then your application can invoke the -link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#addParser-io.helidon.config.spi.ConfigParser-[config builder's `addParser`] method -so that builder will use the parsers you choose. - -You can extend the system with custom parsers of your own. Implement the link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigParser.html[`ConfigParser`] interface, then construct a `Config.Builder` using the `addParser` method, passing an instance of your customer parser. Invoke one of the `sources` methods to include a source that uses the custom format and then build the `Config` object. - -== Detecting and Responding to Changes in Config Data -Each `Config` object which the config system returns to your application is -immutable; even if the information in one of the underlying config sources changes, an in-memory data structure built from the earlier -content remains unchanged. - -Even so, the config system allows your application to learn when such underlying changes in the data occur and respond accordingly. The xref:mutability-support.adoc[mutability] section explains this in detail, and the link:{config-javadoc-base-url}/io/helidon/config/PollingStrategies.html[`PollingStrategies`] JavaDoc describes the built-in implementations. You can, of course, write your own by implementing the link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`] interface. On a config source builder invoke `pollingStrategy` with an instance of your custom strategy and then invoke `build` to create the `ConfigSource`. - -== Dealing with Loading Errors: Retry Policies [[retry]] -Config sources, especially those that depend on fallible mechanisms such as the network or a shared file system, might fail to load during momentary outages. The config system allows you to build resiliency into your application's use of configuration that relies on such technologies. - -When your application builds a `ConfigSource` it can specify a _retry policy_. When the config system needs to load data from that source it delegates the load operation to that retry policy. That policy is responsible not only for loading the data but also for detecting errors during loading and implementing the algorithm for deciding when and how many times to retry a failed load before reporting a failure back to your application. - -The config system includes two predefined retry policies: - -.Predefined Retry Policies -|=== -|Policy | Summary - -|"just call" (default) |asks the config source to load the data with no retry -|"repeat" |performs a settable number of time-based retries, reporting failure only after all available retries have failed -|=== - -See the link:{config-javadoc-base-url}/io/helidon/config/RetryPolicies.html[`RetryPolicies`] JavaDoc for complete details on these built-in retry policies. - -You can devise your own policy. Implement the link:{config-javadoc-base-url}/io/helidon/config/spi/RetryPolicy.html[`RetryPolicy`] interface. Then pass an instance of your policy implementation to the config source builder's `retryPolicy` method. - diff --git a/docs/se/config/extensions.adoc b/docs/se/config/extensions.adoc index 8c89c673361..9d9ec0b157a 100644 --- a/docs/se/config/extensions.adoc +++ b/docs/se/config/extensions.adoc @@ -19,13 +19,29 @@ = Extensions :description: Helidon config extensions :keywords: helidon, config +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + Developer-provided extensions influence how the config system behaves. -The xref:introduction.adoc[config system introduction] explains the design of the config +The xref:introduction.adoc#_getting_started[config system introduction] explains the design of the config system and how its parts work together to read and parse config data, convert it to Java types, fine-tune the look-up of config data, and reload and reprocess data when it changes. _Config extensions_ provided by the application @@ -160,7 +176,7 @@ The following diagram depicts the `Content` API. image::config/spi-content.png[title="Content SPI",align="center"] -Some of the methods provided are not always mandatory, yet they are part of the APIs to simplify the overall class structure: +Some methods provided are not always mandatory, yet they are part of the APIs to simplify the overall class structure: * ConfigContent.stamp() - this method is used by `PollingStrategy` to determine if content has been changed. This can be always @@ -190,7 +206,7 @@ The `ConfigParser.Content` interface defines operations on the content that is t The application can register parsers for a builder by invoking `Config.Builder#addParser(ConfigParser)`. The config system also uses the Java service loader mechanism to load automatically, for all builders, any parsers listed in the `META-INF/services/io.helidon.config.spi.ConfigParser` resource on the runtime classpath. -Prevent auto loading of parsers for a given builder by invoking `Config.Builder#disableParserServices()`. +Prevent autoloading of parsers for a given builder by invoking `Config.Builder#disableParserServices()`. `ConfigParser` accepts `@Priority`. See <>. @@ -250,7 +266,7 @@ link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html[`addFilter` system also uses the Java service loader mechanism to load additional filters automatically, for all builders, using the service interface described in the following table. Prevent a given -builder from using the auto-loaded filters by invoking the +builder from using the autoloaded filters by invoking the link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#disableFilterServices--[`disableFilterServices`] method. @@ -327,7 +343,7 @@ For `Config.as(GenericType)` - the first two steps are skipped. The config system also uses the Java `ServiceLoader` mechanism to load automatically, for all builders, any mappers returned by the providers listed in the `META-INF/services/io.helidon.config.spi.ConfigMapperProvider` resource on the -runtime classpath. The application can prevent auto loading of mappers for a +runtime classpath. The application can prevent autoloading of mappers for a given builder by invoking `Config.Builder#disableMapperServices()`. Note that the built-in mappers described in `ConfigMappers` still operate. @@ -441,7 +457,7 @@ image::config/spi-RetryPolicy.png[title="RetryPolicy SPI",align="center"] The application can try to cancel the overall execution of a `RetryPolicy` by invoking the `RetryPolicy#cancel(boolean mayInterruptIfRunning)` method. Ideally the retry policy implementation should be able to abort the execution of the retry policy, even while a - function call is in progress, but the policy must respond to cancels between function calls. + function call is in progress, but the policy must respond to cancel between function calls. In either case `cancel` returns `true` if the retry was aborted without a successful call to the function, and `false` otherwise, including if the function call had already completed successfully or had previously been successfully canceled. diff --git a/docs/se/config/hierarchical-features.adoc b/docs/se/config/hierarchical-features.adoc index f5150494d68..4a0ca279e39 100644 --- a/docs/se/config/hierarchical-features.adoc +++ b/docs/se/config/hierarchical-features.adoc @@ -19,13 +19,24 @@ = Hierarchical Features :description: Helidon hierarchical features :keywords: helidon, config -:toc: preamble -:toclevels: 2 +:feature-name: Config :rootdir: {docdir}/../.. :imagesdir: {rootdir}/images include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + The config system represents configuration as a tree in memory. Many developers will choose to work directly with config values -- values from the leaves in the tree -- accessing them by their keys. You can also navigate @@ -67,6 +78,27 @@ type `MISSING`. The in-memory config tree contains nodes only of types `OBJECT`, and `VALUE`. ==== +== Configuration Key +Each config node (except the root) has a non-null key. Here is the formal +definition of what keys can be: +[source,abnf] +.The ABNF syntax of config key +---- +config-key = *1( key-token *( "." key-token ) ) + key-token = *( unescaped / escaped ) + unescaped = %x00-2D / %x2F-7D / %x7F-10FFFF + ; %x2E ('.') and %x7E ('~') are excluded from 'unescaped' + escaped = "~" ( "0" / "1" ) + ; representing '~' and '.', respectively +---- + +[IMPORTANT] +========= +To emphasize, the dot character ("`.`") has special meaning as a name separator +in keys. To include a dot as a character in a key escape it as +"`~1`". To include a tilda escape it as "`~0`". +========= + == In-memory Representation of Configuration The following example is in link:https://github.com/lightbend/config/blob/master/HOCON.md[HOCON] (human-optimized config object notation) format. @@ -137,7 +169,7 @@ notation to separate the names of the nodes along the path. The code can navigate one level at a time using chained `get` invocations, each specifying one level of the path to the expected node. Or, you can mix the two styles. -All of the following lines retrieve the same `Config` node. +All the following lines retrieve the same `Config` node. [source,java] .Equivalent Config Retrievals ---- diff --git a/docs/se/config/introduction.adoc b/docs/se/config/introduction.adoc index 407207d691a..4e2c9732492 100644 --- a/docs/se/config/introduction.adoc +++ b/docs/se/config/introduction.adoc @@ -18,16 +18,26 @@ = The Configuration Component :description: Helidon config introduction -:keywords: helidon, config +:keywords: helidon, se, config :feature-name: Config :rootdir: {docdir}/../.. :imagesdir: {rootdir}/images include::{rootdir}/includes/se.adoc[] -The config component provides a Java API to load and process -configuration properties from various sources into a `Config` object which the -application can use to retrieve config data. +== ToC + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon provides a very flexible and comprehensive configuration system, offering you many application configuration choices. The Config component provides a Java API to load and process +configuration data from various sources into a `Config` object which the application can then use. include::{rootdir}/includes/dependencies.adoc[] @@ -42,11 +52,7 @@ include::{rootdir}/includes/dependencies.adoc[] ---- == Getting Started - -=== Introducing the Config System [[introducing-the-config-system]] -A brief overview of the config system helps clarify its different parts -and how they work together. Most applications will typically deal with more -than one of these parts. +A brief overview of the config system helps clarify its different parts and how they work together. Most applications will typically deal with more than one of these parts. image::config/overview.png["Configuration Overview",align="center"] @@ -56,10 +62,68 @@ These are the main parts of the configuration system: - A config source - a location containing configuration data (File, Map, Properties etc.) - A config parser - a component capable of transforming bytes into configuration data (such as JSON content, YAML etc.) -The `Config` system handles configuration data in an in-memory tree that represents the configuration structure and values. +=== Config Sources [[config_sources]] + +Configuration can be loaded from different types of locations and expressed in different formats. The config system includes support for several types of config sources, for example: + +1. Environment variables - the property is a name/value pair. +2. Java system properties - the property is a name/value pair. +3. Resources in the classpath - the contents of the resource is parsed according to its inferred format. +4. File - the contents of the file is parsed according to its inferred format. +5. Directory - each non-directory file in the directory becomes a config entry: the file name is the key. +and the contents of that file are used as the corresponding config String value. +6. A URL resource - contents is parsed according to its inferred format. +7. A variety of in-memory data structures (`String`, `Map`, `Properties`) + +See the JavaDoc for the link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html[`ConfigSources`] +class for a complete list of the built-in config source types and how to use them. + +See the xref:advanced-configuration.adoc#_advanced_config_sources[advanced topics'] page for further information on some more involved aspects of config sources. + +=== Config Parsers [[parsers]] +When it reads configuration text from sources, the config system uses config parsers +to translate that text into the in-memory data structures representing that +configuration. + +The config system includes several built-in parsers, such as for the Java properties, YAML, JSON, and HOCON formats. See xref:#built-in-formats[this section] for +how to change your `pom.xml` to make parsers for those formats available to your +application. Then your application can invoke the +link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#addParser-io.helidon.config.spi.ConfigParser-[config builder's `addParser`] method +so that builder will use the parsers you choose. + +You can extend the system with custom parsers of your own. Implement the link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigParser.html[`ConfigParser`] interface, then construct a `Config.Builder` using the `addParser` method, passing an instance of your customer parser. Invoke one of the `sources` methods to include a source that uses the custom format and then build the `Config` object. + +See the xref:advanced-configuration.adoc#_advanced_config_parsers[advanced topics'] page for further information on some more involved aspects of config parsers. + +== Usage + +=== Default Configuration + +Helidon has an internal configuration, so you are not required to provide any configuration data for your application, though in practice you most likely would. + +In your application code, when you create a default `Config` object, Helidon uses the default configuration . + +[source,Java] +---- +Config config = Config.create(); // <1> +---- +<1> The `Config` object is created with default settings. + +=== Custom Config Sources + +Although the default configuration is very simple to use, your application can take full control of all configuration sources and precedence. You can do so by creating and invoking methods on a `Config.Builder` object to construct a `Config` instance. + +When your application prepares a `Config.Builder` it sets what ``ConfigSource``s and +``ConfigParser``s the builder should use in constructing the resulting `Config` object. The JavaDoc explains how to use the +link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html[`Config.Builder`]. + +See the xref:{rootdir}/se/guides/config.adoc#_custom_configuration_sources[Custom Configuration Sources] and xref:advanced-configuration.adoc#_advanced_config_sources[advanced config sources] sections for detailed examples and further information. + +=== Accessing Config Values + +You have used Helidon to customize configuration behavior from your code using the `Config` and `Config.Builder` classes. As discussed previously, `Config` system reads configuration from a config source, which uses a config parser to translate the source into an in-memory tree which represents the configuration’s structure and values. -This approach allows us to take any source data, be it a flat properties file or an object structure such as JSON, and -transform it into a single tree that allows for overriding of values using heterogeneous config sources. +This approach allows us to take any source data, be it a flat properties file or an object structure such as JSON, and transform it into a single tree that allows for overriding of values using heterogeneous config sources. We are using the `.` as a separator of tree structure. @@ -78,8 +142,10 @@ web: page-size: 25 ---- -The configuration has the same internal representation in `Config` and can be accessed using -the `Config` API as follows: +The configuration has the same internal representation in `Config`. Once created, the `Config` object provides many methods the application can use to +retrieve config data as various Java types. See the link:{config-javadoc-base-url}/io/helidon/config/Config.html[`Config`] +JavaDoc for complete details. + [source,java] ---- int pageSize = config.get("web.page-size") @@ -101,23 +167,21 @@ For this first example we can see the basic features of `Config`: - Configuration is a tree of `Config` nodes - You can use `.` as a tree separator when requesting node values -- Each config value can be retrieved as a typed object, with shortcut methods for the most - commonly used types, such as `int`, `String`, `long` and other -- You can immediately provide a default value for the cases the configuration option is not defined - in any source +- Each config value can be retrieved as a typed object, with shortcut methods for the most commonly used types, such as `int`, `String`, `long` and other +- You can immediately provide a default value for the cases the configuration option is not defined in any source === Overriding Values The `Config` system treats config sources as a hierarchy, where the first source that has a specific configuration key "wins" and its value is used, other sources are not even queried for it. -For example the default configuration when you use `Config.create()` uses the following config sources: +In order to properly configure your application using configuration sources, you need to understand the precedence rules that Helidon uses to merge your configuration data. If any of the Helidon required properties are not specified in one of these source, then Helidon will use a default value. + +For example the default configuration when you use `Config.create()` uses the following config sources in precedence order: 1. System properties config source 2. Environment variables config source -3. A classpath config source called `application.?` where the `?` depends on supported media types - currently on the classpath. By default it is `properties`, if you have YAML support on classpath, - it would be `application.yaml` (a `ConfigParser` may add additional supported suffixes for default file) +3. A classpath config source called `application.?` where the `?` depends on supported media types currently on the classpath.By default, it is `properties`, but if you have YAML support on classpath, it would be `application.yaml` (a `ConfigParser` may add additional supported suffixes for default file) Let's consider the following keys: @@ -128,26 +192,7 @@ Let's consider the following keys: When you request `config.get(`answer`).asInt().orElse(25)`, you would get `42` This allows you to configure environment specific configuration values through -system properties, environment variables, or through files available on each environment (be it -a physical machine, a Kubernetes pod, or a docker image) without changing your source code. - -=== Built-in Support for Config Formats [[built-in-formats]] -If you add additional Helidon config maven artifacts to your dependencies, then the -config system can read formats other than Java properties format and the default -configuration will search for other `application` file types -in the following order. Note that the default configuration _stops_ once it finds -one of the files below; it _does not_ merge all such files it can find. - -.Default Config Files (most to least important) -|=== -|Source |Helidon maven artifact ID (group ID: `io.helidon.config`) |Notes - -|`application.yaml` |`helidon-config-yaml` |YAML format http://yaml.org -|`application.conf` |`helidon-config-hocon` |HOCON format https://github.com/lightbend/config#using-hocon-the-json-superset -|`application.json` |`helidon-config-hocon` |JSON format https://json.org/ -|`application.properties` |`helidon-config` |Java properties format -|=== - +system properties, environment variables, or through files available on each environment (be it a physical machine, a Kubernetes pod, or a docker image) without changing your source code. === Config Filters @@ -171,22 +216,7 @@ The filter resolves the `$\{host}` reference to the `localhost` value. This makes it easier to override values in testing and production, as you can just override the `host` key and leave the URIs same. -=== Change Support - -Config is an immutable in-memory tree. Nevertheless we know that configuration sometimes changes, - and we may want to react to such changes. - -In `Config` system, you can do this through change support provided by these components: - -1. `Config.onChange()` API - you can use to add your listener, to be notified of configuration changes -2. `PollingStrategy` - a component providing regular events to check if a source has changed. This - requires support in config sources themselves (see `PollableSource`) -3. `ChangeWatcher` - a component watching the underlying source for changes. This requires support - in config sources themselves (see `WatchableSource`) -4. `EventConfigSource` - an event source that is capable of notifying about changes itself - - -If you want to receive `onChange` events, you must configure your Config with at least one source that is capable of providing changes (having a `PollingStrategy` or `ChangeWatcher` configured, or implementing `EventConfigSource`) +See xref:advanced-configuration.adoc#filters-and-overrides[Filter, Overrides, and Token Substitution] section for further information on some more involved aspects. === Typed config values @@ -209,33 +239,85 @@ ConfigValue can be used to obtain: The config system automatically knows how to return `List` and `Map` complex types, and you can provide _config mappers_ to convert a config subtree to whatever Java types your application needs. -== Next Steps -Although the default configuration is very simple to use, your -application can take as much control as it needs over +See xref:property-mapping.adoc[Property Mapping] page for details on how to use the built-in mappings and your own custom ones to convert to simple and complex types. -* loading configuration data, -* accessing the data once loaded, and -* extending and modifying the behavior of the config system. +=== Dealing with Loading Errors: Retry Policies [[retry]] +Config sources, especially those that depend on fallible mechanisms such as the network or a shared file system, might fail to load during momentary outages. The config system allows you to build resiliency into your application's use of configuration that relies on such technologies. -You do this by: +When your application builds a `ConfigSource` it can specify a _retry policy_. When the config system needs to load data from that source it delegates the load operation to that retry policy. That policy is responsible not only for loading the data but also for detecting errors during loading and implementing the algorithm for deciding when and how many times to retry a failed load before reporting a failure back to your application. -* creating and invoking methods on a `Config.Builder` object to construct a `Config` instance -+ -Using a builder, the application can control everything about how the config -system creates the resulting `Config` instance: config sources, parsers, polling strategy, -filters, overrides, mappers, whether or not environment variables and Java -system properties serve as config sources. The JavaDoc explains how to use the -link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html[`Config.Builder`]. -+ -* using a xref:config-profiles.adoc[config profile] to choose the sources to be used -* creating a xref:advanced-configuration.adoc#Config-Advanced-metaconfig[meta-configuration] -file on the runtime classpath or file system to control how the config system prepares the -default configuration. +The config system includes two predefined retry policies: -Once created, the `Config` object provides many methods the application can use to -retrieve config data as various Java types. See the link:{config-javadoc-base-url}/io/helidon/config/Config.html[`Config`] -JavaDoc for complete details. +.Predefined Retry Policies +|=== +|Policy | Summary + +|"just call" (default) |asks the config source to load the data with no retry +|"repeat" |performs a settable number of time-based retries, reporting failure only after all available retries have failed +|=== + +See the link:{config-javadoc-base-url}/io/helidon/config/RetryPolicies.html[`RetryPolicies`] JavaDoc for complete details on these built-in retry policies. + +You can devise your own policy. Implement the link:{config-javadoc-base-url}/io/helidon/config/spi/RetryPolicy.html[`RetryPolicy`] interface. Then pass an instance of your policy implementation to the config source builder's `retryPolicy` method. + +=== Change Support + +Each `Config` object which the config system returns to your application is +immutable; even if the information in one of the underlying config sources changes, an in-memory data structure built from the earlier content remains unchanged. + +Nevertheless, we know that configuration sometimes changes, and we may want to react to such changes. So the config system allows your application to learn when such underlying changes in the data occur and respond accordingly. + +In `Config` system, you can do this through change support provided by these components: + +1. `Config.onChange()` API - you can use to add your listener, to be notified of configuration changes +2. `PollingStrategy` - a component providing regular events to check if a source has changed. This +requires support in config sources themselves (see `PollableSource`) +3. `ChangeWatcher` - a component watching the underlying source for changes. This requires support +in config sources themselves (see `WatchableSource`) +4. `EventConfigSource` - an event source that is capable of notifying about changes itself + +If you want to receive `onChange` events, you must configure your Config with at least one source that is capable of providing changes (having a `PollingStrategy` or `ChangeWatcher` configured, or implementing `EventConfigSource`) + +The xref:mutability-support.adoc[mutability] documentation explains this in detail, and the link:{config-javadoc-base-url}/io/helidon/config/PollingStrategies.html[`PollingStrategies`] JavaDoc describes the built-in implementations. + +You can, of course, write your own by implementing the link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`] interface. On a config source builder invoke `pollingStrategy` with an instance of your custom strategy and then invoke `build` to create the `ConfigSource`. + +=== Built-in Support for Config Formats [[built-in-formats]] + +If you add additional Helidon config maven artifacts to your dependencies, then the +config system can read formats other than Java properties format and the default +configuration will search for other `application` file types +in the following order. Note that the default configuration _stops_ once it finds +one of the files below; it _does not_ merge all such files it can find. + +.Default Config Files (most to the least important) +|=== +|Source |Helidon maven artifact ID (group ID: `io.helidon.config`) |Notes + +|`application.yaml` |`helidon-config-yaml` |YAML format http://yaml.org +|`application.conf` |`helidon-config-hocon` |HOCON format https://github.com/lightbend/config#using-hocon-the-json-superset +|`application.json` |`helidon-config-hocon` |JSON format https://json.org/ +|`application.properties` |`helidon-config` |Java properties format +|=== + +You can also extend the config system +to handle other types of sources by implementing the +link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigSource.html[`ConfigSource`] interface. See +the xref:extensions.adoc[extensions'] documentation for complete information. + +== Guides + +[PILLARS] +==== +[CARD] +.SE Config Guide +[link={rootdir}/se/guides/config.adoc] +-- +Step-by-step guide about using Config in your Helidon SE application. +-- +==== +== Additional Information The links in the following tables lead you to more information about various other config topics. @@ -243,13 +325,12 @@ other config topics. |=== | Topic |Documentation -| Where config comes from |xref:config-sources.adoc[Config sources],xref:config-profiles.adoc[Config Profiles], -xref:advanced-configuration.adoc#Config-Advanced-metaconfig[meta-configuration] -| What format config data is expressed in |xref:config-sources.adoc#parsers[Config parsers], +| Where config comes from |xref:#config_sources[Config sources],xref:config-profiles.adoc[Config Profiles] +| What format config data is expressed in |xref:#parsers[Config parsers], xref:supported-formats.adoc[supported formats] | How to filter, override, and dereference values |xref:advanced-configuration.adoc#filters-and-overrides[Filters and overrides] -| What happens when config data changes |xref:mutability-support.adoc#polling[Config polling] -| How to deal with loading errors |xref:config-sources.adoc#retry[Config retry policies] +| What happens when config data changes |xref:mutability-support.adoc[Mutability Support] +| How to deal with loading errors |xref:#retry[Config retry policies] |=== .Accessing Configuration Data diff --git a/docs/se/config/mutability-support.adoc b/docs/se/config/mutability-support.adoc index 5d2e7eeb220..8692c265c07 100644 --- a/docs/se/config/mutability-support.adoc +++ b/docs/se/config/mutability-support.adoc @@ -19,22 +19,27 @@ = Mutability Support :description: Helidon mutability support :keywords: helidon, config +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -An in-memory config tree, once loaded, is immutable, even though the data in the underlying -config sources _can_ change over time. Your application can find out metadata about a -loaded in-memory config and can track changes in config sources. +== ToC + +- <> +- <> +- <> +- <> == Overview -Even though in-memory config trees are immutable, the config system internally +An in-memory config tree, once loaded, is immutable, even though the data in the underlying +config sources _can_ change over time. The config system internally records which config sources it used to load each config tree and some metadata about the configuration. Your application can be aware of updates to the underlying config sources by: 1. using the metadata the config system maintains, -2. responding to changes when the config sources are updated, or +2. responding to change when the config sources are updated, or 3. using ``Supplier``s of particular config values to obtain the always-current value for a key. @@ -82,7 +87,7 @@ Any code you write using the existing `Config.changes()` method might need to change at that time. ==== -Although in-memory config trees do not change once loaded, applications can respond to changes +Although in-memory config trees do not change once loaded, applications can respond to change in the underlying config sources by: 1. setting up change detection for the config sources used to build a configuration, and @@ -240,7 +245,7 @@ to your `onNext` method. The original subscription remains in force for changes to the "new" instance. == Accessing Always-current Values -Some applications do not need to respond to changes as they happen. Instead it's +Some applications do not need to respond to change as they happen. Instead, it's sufficient that they simply have access to the current value for a particular key in the configuration. diff --git a/docs/se/config/property-mapping.adoc b/docs/se/config/property-mapping.adoc index 21111887806..980530a413a 100644 --- a/docs/se/config/property-mapping.adoc +++ b/docs/se/config/property-mapping.adoc @@ -19,10 +19,21 @@ = Property Mapping :description: Helidon config property mapping :keywords: helidon, config +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> +- <> +- <> + +== Overview + Although config values are originally text, you can use the config system's built-in conversions or add your own to translate text into Java primitive types and simple objects (such as `Double`) and to @@ -135,7 +146,7 @@ package JavaDoc]. Some of those approaches require that the target class -- the class to which you want to convert the configuration data -- have certain characteristics or that you add a method to the class to help do the mapping. -You might want to avoid changing the target class or you +You might want to avoid changing the target class else you might not even be able to if you do not control its source. Here are two approaches that will always work without requiring changes @@ -407,8 +418,7 @@ object from the config data. By default, the system matches potential JavaBean property names with config keys in the configuration. -Use the link:{config-javadoc-base-url}/io/helidon/config/Value.html[`Value`] annotation to control some of the JavaBean processing for a -given property. +Use the link:{config-javadoc-base-url}/io/helidon/config/Value.html[`Value`] annotation to control some of JavaBean processing for a given property. .`Value` Annotation |=== @@ -675,7 +685,7 @@ The factory method returns the new initialized `AppConfig` instance. Note the consistent use of `@Value(key = "...")` on each parameter. <3> Because the property `greeting` does not specify a default value the property is **mandatory** and must appear in the configuration source. -Otherwise the config system throws a `ConfigMappingException`. +Otherwise, the config system throws a `ConfigMappingException`. Alternatively, you can use an annotated constructor instead of a static factory method. Revising the example above, make the constructor public, annotate its diff --git a/docs/se/config/supported-formats.adoc b/docs/se/config/supported-formats.adoc index 86df3af7b64..0b5d3ddc0b7 100644 --- a/docs/se/config/supported-formats.adoc +++ b/docs/se/config/supported-formats.adoc @@ -19,21 +19,20 @@ = Additional Supported Formats and Sources :description: Helidon config supported formats and sources :keywords: helidon, config -:toc: preamble -:toclevels: 2 +:feature-name: Config :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +== ToC + +- <> +- <> +- <> + +== Overview Helidon Config provides several extension modules that support other configuration - formats (parsers) and sources. This section describes how to add these modules -to your build and how to use them from your application. - -== Introduction -This document describes the additional config formats and sources the Helidon -config system supports and how to include them and use them in your project. In each -case you need to add module dependencies to your project and, in some cases, -write your application accordingly. +formats (parsers) and sources. This document describes how to include them and use them in your project. In each case you need to add module dependencies to your project and, in some cases, write your application accordingly. == Additional Config Formats and Parsers diff --git a/docs/se/guides/config.adoc b/docs/se/guides/config.adoc index 15f585650fb..3cf963dce6a 100644 --- a/docs/se/guides/config.adoc +++ b/docs/se/guides/config.adoc @@ -98,8 +98,8 @@ In order to properly configure your application using configuration sources, you the precedence rules that Helidon uses to merge your configuration data. By default, Helidon will use the following sources in precedence order: -1. Environment variables -2. Java system properties +1. Java system properties +2. Environment variables 3. Configuration specified in `application.yaml` If any of the Helidon required properties are not specified in one of these source, like `server.port`, then Helidon will use a default value. diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 6f60aae8bf0..48fd3894b6b 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -112,14 +112,13 @@ backend: value: "settings" sources: - "introduction.adoc" - - "config-sources.adoc" - "hierarchical-features.adoc" - "property-mapping.adoc" + - "supported-formats.adoc" + - "config-profiles.adoc" - "mutability-support.adoc" - "advanced-configuration.adoc" - "extensions.adoc" - - "supported-formats.adoc" - - "config-profiles.adoc" - type: "PAGE" title: "CORS" source: "cors.adoc" @@ -329,7 +328,7 @@ backend: value: "settings" sources: - "introduction.adoc" - - "config-sources.adoc" + - "advanced-configuration.adoc" - type: "MENU" title: "CDI Extensions" dir: "extensions" From 32c89ec7e09e5ff96e981e744b7f90954cb96db8 Mon Sep 17 00:00:00 2001 From: Andrii Serkes <74911628+aserkes@users.noreply.github.com> Date: Tue, 19 Jul 2022 00:59:13 +0200 Subject: [PATCH 38/51] Fix Ambiguous dependencies for type PersistenceUnitInfo (#4568) Signed-off-by: aserkes --- .../microprofile-config.properties.mustache | 12 +- .../test/java/__pkg__/MainTest.java.mustache | 5 +- .../main/archetype/mp/custom/custom-mp.xml | 1 + .../src/main/archetype/mp/custom/database.xml | 140 ++++++++++-------- .../__pkg__/PokemonResource.java.mustache | 2 +- .../__pkg__/PokemonTypeResource.java.mustache | 2 +- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/archetypes/helidon/src/main/archetype/mp/common/files/src/main/resources/META-INF/microprofile-config.properties.mustache b/archetypes/helidon/src/main/archetype/mp/common/files/src/main/resources/META-INF/microprofile-config.properties.mustache index 65af88b7964..426b898f95e 100644 --- a/archetypes/helidon/src/main/archetype/mp/common/files/src/main/resources/META-INF/microprofile-config.properties.mustache +++ b/archetypes/helidon/src/main/archetype/mp/common/files/src/main/resources/META-INF/microprofile-config.properties.mustache @@ -9,10 +9,16 @@ metrics.rest-request.enabled=false {{.}} {{/config-entries}} -{{#database}} +{{#database-hikari}} # Datasource properties javax.sql.DataSource.{{ds-name}}.dataSourceClassName={{jdbcDataSource}} -javax.sql.DataSource.{{ds-name}}.dataSource.url=database_url +javax.sql.DataSource.{{ds-name}}.dataSource.url={{databaseUrl}} javax.sql.DataSource.{{ds-name}}.dataSource.user=db_user javax.sql.DataSource.{{ds-name}}.dataSource.password=user_password -{{/database}} \ No newline at end of file +{{/database-hikari}} +{{#database-ucp}} +oracle.ucp.jdbc.PoolDataSource.{{ds-name}}.url={{databaseUrl}} +oracle.ucp.jdbc.PoolDataSource.{{ds-name}}.connectionFactoryClassName={{jdbcDataSource}} +oracle.ucp.jdbc.PoolDataSource.{{ds-name}}.user=db_user +oracle.ucp.jdbc.PoolDataSource.{{ds-name}}.password=user_password +{{/database-ucp}} \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/mp/common/files/src/test/java/__pkg__/MainTest.java.mustache b/archetypes/helidon/src/main/archetype/mp/common/files/src/test/java/__pkg__/MainTest.java.mustache index 9a42f62d2ce..991c3e4bd01 100644 --- a/archetypes/helidon/src/main/archetype/mp/common/files/src/test/java/__pkg__/MainTest.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/common/files/src/test/java/__pkg__/MainTest.java.mustache @@ -17,10 +17,12 @@ import {{.}}; import {{.}}; {{/MainTest-static-imports}} +{{#helidon-test}} @HelidonTest @TestMethodOrder(MethodOrderer.MethodName.class) +{{/helidon-test}} public class MainTest { - +{{#helidon-test}} {{#MainTest-static-fields}} {{.}} {{/MainTest-static-fields}} @@ -31,4 +33,5 @@ public class MainTest { {{#MainTest-methods}} {{.}} {{/MainTest-methods}} +{{/helidon-test}} } diff --git a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml index 0286e476465..d7982d0318c 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml @@ -37,6 +37,7 @@ + true Minimal Helidon MP project suitable to start from scratch. diff --git a/archetypes/helidon/src/main/archetype/mp/custom/database.xml b/archetypes/helidon/src/main/archetype/mp/custom/database.xml index e9051daf8b4..67190a76383 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/database.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/database.xml @@ -143,6 +143,7 @@ - - - diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonResource.java.mustache index a45dec90a04..da3473077f7 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonResource.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonResource.java.mustache @@ -34,7 +34,7 @@ import java.util.List; @Path("pokemon") public class PokemonResource { - @PersistenceContext(unitName = "test") + @PersistenceContext(unitName = "{{pu-name}}") private EntityManager entityManager; @GET diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonTypeResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonTypeResource.java.mustache index 52d9938024a..8fef249505e 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonTypeResource.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/PokemonTypeResource.java.mustache @@ -22,7 +22,7 @@ import jakarta.ws.rs.core.MediaType; @Path("type") public class PokemonTypeResource { - @PersistenceContext(unitName = "test") + @PersistenceContext(unitName = "{{pu-name}}") private EntityManager entityManager; @GET From 26b0137b2763a8cc3d9d263eb7417026cece5726 Mon Sep 17 00:00:00 2001 From: Arjav Desai Date: Mon, 18 Jul 2022 22:18:05 -0500 Subject: [PATCH 39/51] [New Docs PR] Integration Docs (#4566) --- docs/includes/oci.adoc | 70 +++++ docs/mp/{vault.adoc => integrations/hcv.adoc} | 211 +++++++------- docs/mp/{oci => integrations}/oci.adoc | 83 +++++- docs/mp/oci/atp.adoc | 81 ------ docs/mp/oci/object-storage.adoc | 92 ------- docs/mp/oci/vault.adoc | 103 ------- docs/se/{vault.adoc => integrations/hcv.adoc} | 154 +++++------ docs/se/integrations/oci.adoc | 114 ++++++++ docs/se/oci/atp.adoc | 129 --------- docs/se/oci/object-storage.adoc | 258 ----------------- docs/se/oci/oci.adoc | 84 ------ docs/se/oci/vault.adoc | 260 ------------------ docs/sitegen.yaml | 28 +- examples/integrations/oci/README.md | 11 + 14 files changed, 444 insertions(+), 1234 deletions(-) create mode 100644 docs/includes/oci.adoc rename docs/mp/{vault.adoc => integrations/hcv.adoc} (86%) rename docs/mp/{oci => integrations}/oci.adoc (57%) delete mode 100644 docs/mp/oci/atp.adoc delete mode 100644 docs/mp/oci/object-storage.adoc delete mode 100644 docs/mp/oci/vault.adoc rename docs/se/{vault.adoc => integrations/hcv.adoc} (94%) create mode 100644 docs/se/integrations/oci.adoc delete mode 100644 docs/se/oci/atp.adoc delete mode 100644 docs/se/oci/object-storage.adoc delete mode 100644 docs/se/oci/oci.adoc delete mode 100644 docs/se/oci/vault.adoc create mode 100644 examples/integrations/oci/README.md diff --git a/docs/includes/oci.adoc b/docs/includes/oci.adoc new file mode 100644 index 00000000000..a3f831e84c3 --- /dev/null +++ b/docs/includes/oci.adoc @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] + +== Resolving javax and jakarta package compatibility issue with OCI SDK + +With Helidon 3, we are now implementing the MicroProfile 5 Platform and selected Jakarta EE 9.1 specifications. We are also going away from javax.* packages and fully embracing jakarta.* package. + +However, the current release 2.35.0 of OCI Java SDK is still using javax.* packages which created compatibility issues e.g. Helidon 3 uses JAX-RS 3.0.0 (jakarta package names) and the corresponding Jersey implementation. OCI SDK 2.35.0 uses JAX-RS Client 2.1.6 (javax package names) and the corresponding Jersey implementation. Therefore, the OCI SDK is incompatible with Helidon 3 applications and any application that uses JAX-RS 3. We have filed an issue with OCI SDK team, see https://github.com/oracle/oci-java-sdk/issues/371 for details on this. + +OCI SDK team has provided us with `shaded` jar as workaround as mentioned in the issue. However, this jar is not available in maven-central as of now. See issue https://github.com/oracle/oci-java-sdk/issues/410 for details on that, so user will have to do manual setup/install of this `shaded` jar. You only have to do this once per version of SDK. + +* Download OCI SDK from https://github.com/oracle/oci-java-sdk/releases/download/v2.35.0/oci-java-sdk-2.35.0.zip +* Unzip the SDK +[source,bash] +---- +unzip -j -p oci-java-sdk-2.35.0.zip shaded/lib/oci-java-sdk-full-shaded-2.35.0.jar > oci-java-sdk-shaded-full-2.35.0.jar +---- +* Install `shaded` jar in your local maven repo, see https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html +[source,bash] +---- +mvn -N org.apache.maven.plugins:maven-install-plugin:install-file -Dfile=oci-java-sdk-shaded-full-2.35.0.jar -DlocalRepositoryPath="~/.m2/repository" -DgroupId=com.oracle.oci.sdk -DartifactId=oci-java-sdk-shaded-full -Dversion=2.35.0 -Dpackaging=jar +---- + +Now, when you want to use modules from OCI SDK in your application, instead of using individual modules as defined in our OCI integration documentation, you need to use `full shaded` jar. + +[source,xml] +---- + + com.oracle.oci.sdk + oci-java-sdk-shaded-full + 2.35.0 + +---- + +Since the `full shaded` jar doesn't bring in its transitive dependencies, you will also need to define following dependencies in your application so that it works at runtime. + +[source,xml] +---- + + org.bouncycastle + bcpkix-jdk15on + 1.70 + runtime + + + org.slf4j + slf4j-jdk14 + 1.7.32 + runtime + +---- + +Please refer to our link:{helidon-github-tree-url}/examples/integrations/oci[OCI SDK Usage Examples] to see this in action. \ No newline at end of file diff --git a/docs/mp/vault.adoc b/docs/mp/integrations/hcv.adoc similarity index 86% rename from docs/mp/vault.adoc rename to docs/mp/integrations/hcv.adoc index 5c0c297f493..d59aecda93e 100644 --- a/docs/mp/vault.adoc +++ b/docs/mp/integrations/hcv.adoc @@ -16,153 +16,140 @@ /////////////////////////////////////////////////////////////////////////////// -= Vault -:description: Helidon Vault integration -:keywords: vault -:feature-name: Vault -:rootdir: {docdir}/.. += HashiCorp Vault +:description: Helidon HashiCorp Vault integration +:keywords: vault, hashicorp +:feature-name: HashiCorp Vault +:rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] -HashiCorp Vault is a commonly used Vault in many microservices. The APIs are REST-based and Helidon implements them using reactive client. - -Vault integration supports the following: - -* *Secret Engines*: Key/Value version 2, Key/Value version 1, Cubbyhole, PKI, Transit, Database -* *Authentication Methods*: Token, Kubernetes (k8s), AppRole -* *Other Sys Operations and Configurations* - -== Experimental - -WARNING: Helidon Vault support is still experimental and not intended for production use. APIs and features have not yet been fully tested and are subject to change. - -== Sys Operations - -Each of these features is implemented as a separate module, with the Vault class binding them together. In Helidon MP, with injection, this binding is done automatically, and you can simply inject your favorite secret engine. - -In addition to these features, Vault itself can be authenticated as follows: +== ToC -* Token authentication - token is configured when connecting to Vault -* AppRole authentication - AppRole ID and secret ID are configured, integration exchanges these for a temporary token that is used to connect to Vault -* K8s authentication - the k8s JWT token is discovered on current node and used to obtain a temporary token that is used to connect to Vault - -== Extensibility +- <> +- <> +- <> +- <> +- <> +- <> -New secret engines and authentication methods can be implemented quite easily, as the integration is based on service providers (using ServiceLoader). This gives us (or you, as the users) the option to add new secret engines and/or authentication methods without adding a plethora of methods to the Vault class. - -See the following SPIs: +== Overview -[source,listing] ----- -io.helidon.integrations.vault.spi.AuthMethodProvider -io.helidon.integrations.vault.spi.SecretsEngineProvider -io.helidon.integrations.vault.spi.SysProvider -io.helidon.integrations.vault.spi.VaultAuth -io.helidon.integrations.vault.spi.InjectionProvider ----- - -== Modules +HashiCorp Vault is a commonly used Vault in many microservices. The APIs are REST-based and Helidon implements them using reactive client. -The following is a list of maven coordinates of all Vault modules available: +include::{rootdir}/includes/dependencies.adoc[] [source,xml] ---- - - - io.helidon.integrations.vault - helidon-integrations-vault - - - io.helidon.integrations.vault.auths - helidon-integrations-vault-auths-token - - - io.helidon.integrations.vault.auths - helidon-integrations-vault-auths-approle - - - io.helidon.integrations.vault.auths - helidon-integrations-vault-auths-k8s - - - io.helidon.integrations.vault.secrets - helidon-integrations-vault-secrets-kv1 - - - io.helidon.integrations.vault.secrets - helidon-integrations-vault-secrets-kv2 - - - io.helidon.integrations.vault.secrets - helidon-integrations-vault-secrets-cubbyhole - - - io.helidon.integrations.vault.secrets - helidon-integrations-vault-secrets-transit - - - io.helidon.integrations.vault.secrets - helidon-integrations-vault-secrets-database - - - io.helidon.integrations.vault.sys - helidon-integrations-vault-sys - - + + io.helidon.integrations.vault + helidon-integrations-vault-cdi + ---- -To use Vault integration in MP, the following module must be added, as it enables injection of all features: +The following is a list of maven coordinates of all Vault modules available: [source,xml] ---- - io.helidon.integrations.vault - helidon-integrations-vault-cdi + io.helidon.integrations.vault.auths + helidon-integrations-vault-auths-token + + + io.helidon.integrations.vault.auths + helidon-integrations-vault-auths-approle + + + io.helidon.integrations.vault.auths + helidon-integrations-vault-auths-k8s + + + io.helidon.integrations.vault.secrets + helidon-integrations-vault-secrets-kv1 + + + io.helidon.integrations.vault.secrets + helidon-integrations-vault-secrets-kv2 + + + io.helidon.integrations.vault.secrets + helidon-integrations-vault-secrets-cubbyhole + + + io.helidon.integrations.vault.secrets + helidon-integrations-vault-secrets-transit + + + io.helidon.integrations.vault.secrets + helidon-integrations-vault-secrets-database + + + io.helidon.integrations.vault.sys + helidon-integrations-vault-sys ---- -Configuration to connect to Vault. +== Usage + +Vault integration supports the following: + +* *Secret Engines*: Key/Value version 2, Key/Value version 1, Cubbyhole, PKI, Transit, Database +* *Authentication Methods*: Token, Kubernetes (k8s), AppRole +* *Other Sys Operations and Configurations* -. Authenticating using Token: -+ +Each of these features is implemented as a separate module, with the Vault class binding them together. In Helidon MP, with injection, this binding is done automatically, and you can simply inject your favorite secret engine. + +The following classes can be injected into any CDI bean (if appropriate module is on the classpath): + +* Kv2Secrets - Key/Value Version 2 Secrets (versioned secrets, default) +* Kv1Secrets - Key/Value Version 1 Secrets (un-versioned secrets, legacy) +* CubbyholeSecrets - Cubbyhole secrets (token bound secrets) +* DbSecrets - Database secrets (for generating temporary DB credentials) +* PkiSecrets - PKI secrets (for generating keys and X.509 certificates) +* TransitSecrets - Transit operations (encryption, signatures, HMAC) +* AppRoleAuth - AppRole authentication method (management operations) +* K8sAuth - Kubernetes authentication method (management operations) +* TokenAuth - Token authentication method (management operations) +* Sys - System operations (management of Vault - enabling/disabling secret engines and authentication methods) +* *Rx - reactive counterparts to the classes defined above, usually not recommended in CDI, as it is a blocking environment + +In addition to these features, Vault itself can be authenticated as follows: + +* Token authentication - token is configured when connecting to Vault [source,properties] ---- vault.address=http://localhost:8200 vault.token=my-token ---- -+ -. Authenticating using AppRole: -+ +* AppRole authentication - AppRole ID and secret ID are configured, integration exchanges these for a temporary token that is used to connect to Vault [source,properties] ---- vault.auth.app-role.role-id=app-role-id vault.auth.app-role.secret-id=app-role-secret-id ---- -+ -. Authenticating using Kubernetes: -+ +* K8s authentication - the k8s JWT token is discovered on current node and used to obtain a temporary token that is used to connect to Vault [source,properties] ---- vault.auth.k8s.token-role=my-role <1> ---- <1> The token role must be configured in Vault -The following classes can be injected into any CDI bean (if appropriate module is on the classpath): +=== Extensibility -* Kv2Secrets - Key/Value Version 2 Secrets (versioned secrets, default) -* Kv1Secrets - Key/Value Version 1 Secrets (un-versioned secrets, legacy) -* CubbyholeSecrets - Cubbyhole secrets (token bound secrets) -* DbSecrets - Database secrets (for generating temporary DB credentials) -* PkiSecrets - PKI secrets (for generating keys and X.509 certificates) -* TransitSecrets - Transit operations (encryption, signatures, HMAC) -* AppRoleAuth - AppRole authentication method (management operations) -* K8sAuth - Kubernetes authentication method (management operations) -* TokenAuth - Token authentication method (management operations) -* Sys - System operations (management of Vault - enabling/disabling secret engines and authentication methods) -* *Rx - reactive counterparts to the classes defined above, usually not recommended in CDI, as it is a blocking environment +New secret engines and authentication methods can be implemented quite easily, as the integration is based on service providers (using ServiceLoader). This gives us (or you, as the users) the option to add new secret engines and/or authentication methods without adding a plethora of methods to the Vault class. +See the following SPIs: -== Usage +[source,listing] +---- +io.helidon.integrations.vault.spi.AuthMethodProvider +io.helidon.integrations.vault.spi.SecretsEngineProvider +io.helidon.integrations.vault.spi.SysProvider +io.helidon.integrations.vault.spi.VaultAuth +io.helidon.integrations.vault.spi.InjectionProvider +---- + +== Examples The following example shows usage of Vault to encrypt a secret using the default Vault configuration (in a JAX-RS resource): @@ -529,7 +516,7 @@ public class TransitResource { <9> Verify HMAC. <10> Verify signature. -== Local testing +== Local Testing [[Local-Testing]] Vault is available as a docker image, so to test locally, you can simply: @@ -538,4 +525,8 @@ Vault is available as a docker image, so to test locally, you can simply: docker run -e VAULT_DEV_ROOT_TOKEN_ID=my-token -d --name=vault -p8200:8200 vault ---- -This will create a Vault docker image, run it in background and open it on localhost:8200 with a custom root token my-token, using name vault. This is of course only suitable for local testing, as the root token has too many rights, but it can be easily used with the examples below. +This will create a Vault docker image, run it in background and open it on `localhost:8200` with a custom root token my-token, using name vault. This is of course only suitable for local testing, as the root token has too many rights, but it can be easily used with the examples below. + +== References + +* link:{helidon-github-tree-url}/examples/integrations/vault[Hashicorp Vault Usage Examples] \ No newline at end of file diff --git a/docs/mp/oci/oci.adoc b/docs/mp/integrations/oci.adoc similarity index 57% rename from docs/mp/oci/oci.adoc rename to docs/mp/integrations/oci.adoc index 8cf48bd7261..eb3869693f2 100644 --- a/docs/mp/oci/oci.adoc +++ b/docs/mp/integrations/oci.adoc @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////// + = Oracle Cloud Infrastructure Integration :description: Helidon OCI Integration :keywords: oci, cdi @@ -24,8 +25,20 @@ include::{rootdir}/includes/mp.adoc[] +== ToC + +- <> +- <> +- <> +- <> +- <> + +== Overview + Helidon MP OCI Integration provides easy access to Oracle Cloud Infrastructure using the OCI Java SDK. +NOTE: OCI SDK uses JAX-RS Client 2.1.6 (javax package names), which makes it incompatible with Helidon 3 applications and any application that uses JAX-RS 3 (jakarta package naming). Please see our link:{rootdir}/includes/oci.adoc[Guide] for detailed information on how to work around this issue. + include::{rootdir}/includes/dependencies.adoc[] [source,xml] @@ -36,24 +49,24 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== The Helidon OCI SDK Extension +== Usage -When added to your application this link:{jakarta-cdi-spec-url}#spi[CDI portable extension] provides support for +When added to your application Helidon OCI SDK link:{jakarta-cdi-spec-url}#spi[CDI portable extension] provides support for injecting link:{oci-javasdk-url}[Oracle Cloud Infrastructure SDK Clients] in your Helidon MicroProfile application. The extension also handles authenticating with OCI by automatically picking up OCI credentials from your environment. -== Configuring the Helidon OCI SDK Extension +=== Configuring the Helidon OCI SDK Extension When you inject an OCI SDK Client object, the Helidon OCI SDK extension configures and constructs the object for you. The configuration primarily -consists of initializing an OCI `AuthenticationDetailsProvider`. By default +consists of initializing an OCI `AuthenticationDetailsProvider`. By default, the extension will examine your environment and select the best `AuthenticationDetailsProvider` and configure it for you. This means if your environment is already set up to work with the OCI SDK or the OCI command line, then it is very likely you do not need to do any additional -configuration of the extension. Just add it as a dependency and it will self-configure. +configuration of the extension. Just add it as a dependency, and it will self-configure. If for some reason you require full control over the OCI configuration you have that as well. For more information concerning the extension and its configuration @@ -62,7 +75,7 @@ link:{integration-oci-sdk-cdi-javadoc-base-url}/io/helidon/integrations/oci/sdk/ javadocs. In particular the `oci.auth-strategies` property lets you control which `AuthenticationDetailsProvider` will be used. -== Accessing OCI Services +=== Accessing OCI Services Since the Helidon OCI SDK extension supports injecting any OCI client from the OCI SDK, you can use it to access any OCI service supported by the @@ -74,9 +87,57 @@ You will also need to configure your environment to authenticate with OCI. It is recommended that you do this first, and verify your configuration by using the link:{oci-javasdk-url}[OCI CLI] to access the service. -The following documentation will help you get started with some -common OCI Services: +== Examples + +This example describes how to use Helidon OCI SDK Extension to access OCI Object Storage. + +As mentioned above in xref:#_accessing_oci_services[], you need to add a dependency on the OCI SDK +Object Storage API: + +[source,xml] +---- + + com.oracle.oci.sdk + oci-java-sdk-objectstorage + +---- + +=== Injecting an Object Storage Client + +Now you can inject OCI SDK Clients. + +[source,java] +.Field-injection example +---- +@Inject +private ObjectStorage client; +---- + +[source,java] +.Constructor-injection example +---- +public class MyClass { + + private final ObjectStorage client; + + @Inject + public YourConstructor(@Named("orders") ObjectStorage client) { + this.client = client; + } +} +---- + +The extension implements this injection point by creating an Object Storage client +object in the link:{jakarta-inject-javadoc-url}/jakarta/inject/Singleton.html[singleton scope]. + +=== Using the Object Storage client + +Once you have injected an ObjectStorage client you can use it as described in: + +* link:{oci-javasdk-objstore-javadoc-base-url}/package-summary.html[OCI SDK Object Storage Javadocs] +* link:{oci-objstore-url}[OCI Object Storage Overview] + +== References -- xref:object-storage.adoc[OCI Object Storage] -- xref:vault.adoc[OCI Vault] -- xref:atp.adoc[OCI ATP] +* link:{integration-oci-sdk-cdi-javadoc-base-url}/io/helidon/integrations/oci/sdk/cdi/OciExtension.html[OciExtension] Javadocs +* link:{helidon-github-tree-url}/examples/integrations/oci[OCI SDK Usage Examples] \ No newline at end of file diff --git a/docs/mp/oci/atp.adoc b/docs/mp/oci/atp.adoc deleted file mode 100644 index df84b9d9e5a..00000000000 --- a/docs/mp/oci/atp.adoc +++ /dev/null @@ -1,81 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI ATP -:description: Helidon OCI ATP integration -:keywords: oci, atp -:feature-name: OCI Autonomous Transaction Processing -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -You can use Helidon's xref:oci.adoc[OCI SDK Extension] to access OCI Services. -This document describes how to use it to access the OCI Database Service. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci.sdk - helidon-integrations-oci-sdk-cdi - ----- - -Then add dependencies on the OCI SDK's Database API. Your specific -dependencies may differ depending on the OCI SDK features you use. - -[source,xml] ----- - - com.oracle.oci.sdk - oci-java-sdk-database - ----- - -== Injecting a Database client - -Once you have Helidon's OCI extension added to your application you can inject -OCI SDK Clients. - -[source,java] -.Field-injection example ----- -@Inject -private Database database; ----- - -The extension implements these injection points by creating -objects in the link:{jakarta-inject-javadoc-url}/jakarta/inject/Singleton.html[singleton scope]. - -== Configuring the Helidon OCI SDK Extension - -By default the extension will select and configure an appropriate -OCI Authentication Details Provider for you based on your environment. -For this reason it is recommended that you configure your environment -first and get it working with the link:{oci-javasdk-url}[OCI CLI] -before using the Helidon OCI SDK Extension. - -For more information see xref:oci.adoc[Helidon OCI Extension]. - -== Using the Database client - -Once you have injected OCI Database objects you can use them as described in: - -* link:{oci-javasdk-database-javadoc-base-url}/package-summary.html[OCI SDK Database Javadocs] -* link:{oci-database-url}[OCI Database Overview] diff --git a/docs/mp/oci/object-storage.adoc b/docs/mp/oci/object-storage.adoc deleted file mode 100644 index c14e7cc65db..00000000000 --- a/docs/mp/oci/object-storage.adoc +++ /dev/null @@ -1,92 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI Object Storage -:description: Helidon OCI Object Storage integration -:keywords: oci, objectstorage -:feature-name: OCI Object Storage -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -You can use Helidon's xref:oci.adoc[OCI SDK Extension] to access OCI Services. -This document describes how to use it to access OCI Object Storage. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci.sdk - helidon-integrations-oci-sdk-cdi - ----- - -Then add a dependency on the OCI SDK's Object Storage API: - -[source,xml] ----- - - com.oracle.oci.sdk - oci-java-sdk-objectstorage - ----- - -== Injecting an Object Storage client - -Once you have Helidon's OCI extension added to your application you can inject OCI SDK Clients. - -[source,java] -.Field-injection example ----- -@Inject -private ObjectStorage client; ----- - -[source,java] -.Constructor-injection example ----- -public class MyClass { - - private final ObjectStorage client; - - @Inject - public YourConstructor(@Named("orders") ObjectStorage client) { - this.client = client; - } -} ----- - -The extension implements this injection point by creating an Object Storage client -object in the link:{jakarta-inject-javadoc-url}/jakarta/inject/Singleton.html[singleton scope]. - -== Configuring the Helidon OCI SDK Extension - -By default the extension will select and configure an appropriate -OCI Authentication Details Provider for you based on your environment. -For this reason it is recommended that you configure your environment first and get it working with the -link:{oci-javasdk-url}[OCI CLI] before using the Helidon OCI SDK Extension. - -For more information see xref:oci.adoc[Helidon OCI Extension]. - -== Using the Object Storage client - -Once you have injected an ObjectStorage client you can use it as described in: - -* link:{oci-javasdk-objstore-javadoc-base-url}/package-summary.html[OCI SDK Object Storage Javadocs] -* link:{oci-objstore-url}[OCI Object Storage Overview] diff --git a/docs/mp/oci/vault.adoc b/docs/mp/oci/vault.adoc deleted file mode 100644 index 7f7dffd0592..00000000000 --- a/docs/mp/oci/vault.adoc +++ /dev/null @@ -1,103 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI Vault -:description: Helidon OCI Vault integration -:keywords: oci, vault -:feature-name: OCI Vault -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -You can use Helidon's xref:oci.adoc[OCI SDK Extension] to access OCI Services. -This document describes how to use it to access OCI Vault. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci.sdk - helidon-integrations-oci-sdk-cdi - ----- - -Then add dependencies on the OCI SDK's Vault API. -Your specific dependencies may differ depending on the OCI SDK features you use. - -[source,xml] ----- - - - com.oracle.oci.sdk - oci-java-sdk-vault - - - com.oracle.oci.sdk - oci-java-sdk-keymanagement - - - com.oracle.oci.sdk - oci-java-sdk-secrets - - ----- - -== Injecting a Vault client - -Once you have Helidon's OCI extension added to your application you can inject OCI SDK Clients. - -[source,java] -.Field-injection example ----- -@Inject -private Vaults vaults; ----- - -[source,java] -.Constructor-injection example ----- -class VaultResource { - @Inject - VaultResource(Secrets secrets, KmsCrypto crypto, Vaults vaults) { - this.secrets = secrets; - this.crypto = crypto; - this.vaults = vaults; - } -} ----- - -The extension implements these injection points by creating objects in the - link:{jakarta-inject-javadoc-url}/jakarta/inject/Singleton.html[singleton scope]. - -== Configuring the Helidon OCI SDK Extension - -By default the extension will select and configure an appropriate OCI Authentication Details Provider for you based - on your environment. - -For this reason it is recommended that you configure your environment first and get it working with the - link:{oci-javasdk-url}[OCI CLI] before using the Helidon OCI SDK Extension. - -For more information see xref:oci.adoc[Helidon OCI Extension]. - -== Using the Vault client - -Once you have injected OCI Vault objects you can use them as described in: - -* link:{oci-javasdk-vault-javadoc-base-url}/package-summary.html[OCI SDK Vault Javadocs] -* link:{oci-vault-url}[OCI Vault Overview] diff --git a/docs/se/vault.adoc b/docs/se/integrations/hcv.adoc similarity index 94% rename from docs/se/vault.adoc rename to docs/se/integrations/hcv.adoc index 5a913d6f80b..db7cd5aecc5 100644 --- a/docs/se/vault.adoc +++ b/docs/se/integrations/hcv.adoc @@ -16,60 +16,41 @@ /////////////////////////////////////////////////////////////////////////////// -= Vault -:description: Helidon Vault integration -:keywords: vault -:feature-name: Vault -:rootdir: {docdir}/.. += HashiCorp Vault +:description: Helidon HashiCorp Vault integration +:keywords: vault, hashicorp +:feature-name: HashiCorp Vault +:rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -HashiCorp Vault is a commonly used Vault in many microservices. The APIs are REST-based and Helidon implements them using reactive client. - -Vault integration supports the following: - -* *Secret Engines*: Key/Value version 2, Key/Value version 1, Cubbyhole, PKI, Transit, Database -* *Authentication Methods*: Token, Kubernetes (k8s), AppRole -* *Other Sys Operations and Configurations* +== ToC -== Experimental +- <> +- <> +- <> +- <> +- <> +- <> -WARNING: Helidon Vault support is still experimental and not intended for production use. APIs and features have not yet been fully tested and are subject to change. - -== Sys Operations - -Each of these features is implemented as a separate module, with the Vault class binding them together. In Helidon MP, with injection, this binding is done automatically, and you can simply inject your favorite secret engine. - -In addition to these features, Vault itself can be authenticated as follows: - -* Token authentication - token is configured when connecting to Vault -* AppRole authentication - AppRole ID and secret ID are configured, integration exchanges these for a temporary token that is used to connect to Vault -* K8s authentication - the k8s JWT token is discovered on current node and used to obtain a temporary token that is used to connect to Vault +== Overview -== Extensibility +HashiCorp Vault is a commonly used Vault in many microservices. The APIs are REST-based and Helidon implements them using reactive client. -New secret engines and authentication methods can be implemented quite easily, as the integration is based on service providers (using ServiceLoader). This gives us (or you, as the users) the option to add new secret engines and/or authentication methods without adding a plethora of methods to the Vault class. +include::{rootdir}/includes/dependencies.adoc[] -See the following SPIs: -[source,properties] +[source,xml] ---- -io.helidon.integrations.vault.spi.AuthMethodProvider -io.helidon.integrations.vault.spi.SecretsEngineProvider -io.helidon.integrations.vault.spi.SysProvider -io.helidon.integrations.vault.spi.VaultAuth -io.helidon.integrations.vault.spi.InjectionProvider + + io.helidon.integrations.vault + helidon-integrations-vault + ---- -== Modules - The following is a list of maven coordinates of all Vault modules available: [source,xml] ---- - - io.helidon.integrations.vault - helidon-integrations-vault - io.helidon.integrations.vault.auths helidon-integrations-vault-auths-token @@ -108,19 +89,43 @@ The following is a list of maven coordinates of all Vault modules available: ---- -Configuration to connect to Vault. +== Usage + +Vault integration supports the following: + +* *Secret Engines*: Key/Value version 2, Key/Value version 1, Cubbyhole, PKI, Transit, Database +* *Authentication Methods*: Token, Kubernetes (k8s), AppRole +* *Other Sys Operations and Configurations* + +Each of these features is implemented as a separate module, with the Vault class binding them together. Code to set up Vault and obtain a specific secret engine: + +[source,java] +---- +Vault vault = Vault.builder() + .config(config.get("vault")) + .build(); +Kv2SecretsRx secrets = vault.secrets(Kv2SecretsRx.ENGINE); +---- + +Similar code can be used for any secret engine available: + +* Kv2SecretsRx - Key/Value Version 2 Secrets (versioned secrets, default) +* Kv1SecretsRx - Key/Value Version 1 Secrets (unversioned secrets, legacy) +* CubbyholeSecretsRx - Cubbyhole secrets (token bound secrets) +* DbSecretsRx - Database secrets (for generating temporary DB credentials) +* PkiSecretsRx - PKI secrets (for generating keys and X.509 certificates) +* TransitSecretsRx - Transit operations (encryption, signatures, HMAC) -. Authenticating using Token: -+ +In addition to these features, Vault itself can be authenticated as follows: + +* Token authentication - token is configured when connecting to Vault [source,yaml] ---- vault: address: "http://localhost:8200" token: "my-token" ---- -+ -. Authenticating using AppRole: -+ +* AppRole authentication - AppRole ID and secret ID are configured, integration exchanges these for a temporary token that is used to connect to Vault [source,yaml] ---- vault: @@ -129,9 +134,7 @@ vault: role-id: "app-role-id" secret-id: app-role-secret-id ---- -+ -. Authenticating using Kubernetes: -+ +* K8s authentication - the k8s JWT token is discovered on current node and used to obtain a temporary token that is used to connect to Vault [source,yaml] ---- vault: @@ -142,53 +145,32 @@ vault: <1> The token role must be configured in Vault Minimal configuration to connect to Vault: -[source,yaml] ----- -vault: - token: "my-token" - address: "http://localhost:8200" ----- - -Code to set up Vault and obtain a specific secret engine: +Code to get the Sys operations of Vault: [source,java] ---- -Vault vault = Vault.builder() - .config(config.get("vault")) - .build(); -Kv2SecretsRx secrets = vault.secrets(Kv2SecretsRx.ENGINE); +SysRx sys = vault.sys(SysRx.API); ---- -Similar code can be used for any secret engine available: +=== Extensibility -* Kv2SecretsRx - Key/Value Version 2 Secrets (versioned secrets, default) -* Kv1SecretsRx - Key/Value Version 1 Secrets (unversioned secrets, legacy) -* CubbyholeSecretsRx - Cubbyhole secrets (token bound secrets) -* DbSecretsRx - Database secrets (for generating temporary DB credentials) -* PkiSecretsRx - PKI secrets (for generating keys and X.509 certificates) -* TransitSecretsRx - Transit operations (encryption, signatures, HMAC) - -Code to obtain a specific authentication method: +New secret engines and authentication methods can be implemented quite easily, as the integration is based on service providers (using ServiceLoader). This gives us (or you, as the users) the option to add new secret engines and/or authentication methods without adding a plethora of methods to the Vault class. -[source,java] +See the following SPIs: +[source,properties] ---- -K8sAuthRx auth = vault.auth(K8sAuthRx.AUTH_METHOD) +io.helidon.integrations.vault.spi.AuthMethodProvider +io.helidon.integrations.vault.spi.SecretsEngineProvider +io.helidon.integrations.vault.spi.SysProvider +io.helidon.integrations.vault.spi.VaultAuth +io.helidon.integrations.vault.spi.InjectionProvider ---- -Similar code can be used for any authentication method available: - -* AppRoleAuthRx - AppRole authentication method (management operations) -* K8sAuthRx - Kubernetes authentication method (management operations) -* TokenAuthRx - Token authentication method (management operations) - -Code to get the Sys operations of Vault: +== Examples -[source,java] ----- -SysRx sys = vault.sys(SysRx.API); ----- +The following example shows usage of Vault to encrypt a secret. -== Usage with WebServer +=== Usage with WebServer Configure the `Vault` object using token base configuration: @@ -580,7 +562,7 @@ class K8sExample { <3> Disable Kubernetes authentication if needed. <4> Function used to enable Kubernetes authentication. -== Local testing +== Local testing [[Local-Testing]] Vault is available as a docker image, so to test locally, you can simply: @@ -590,3 +572,7 @@ docker run -e VAULT_DEV_ROOT_TOKEN_ID=my-token -d --name=vault -p8200:8200 vault ---- This will create a Vault docker image, run it in background and open it on localhost:8200 with a custom root token my-token, using name vault. This is of course only suitable for local testing, as the root token has too many rights, but it can be easily used with the examples below. + +== References + +* link:{helidon-github-tree-url}/examples/integrations/vault[Hashicorp Vault Usage Examples] \ No newline at end of file diff --git a/docs/se/integrations/oci.adoc b/docs/se/integrations/oci.adoc new file mode 100644 index 00000000000..87d67c3fe9d --- /dev/null +++ b/docs/se/integrations/oci.adoc @@ -0,0 +1,114 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 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. + +/////////////////////////////////////////////////////////////////////////////// + += Oracle Cloud Infrastructure Integration +:description: Helidon OCI Integration +:keywords: oci +:feature-name: OCI Integration +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/se.adoc[] + +== ToC + +- <> +- <> +- <> +- <> + +== Overview + +Helidon SE OCI Integration provides easy access to Oracle Cloud Infrastructure using the OCI Java SDK. + +NOTE: OCI SDK uses JAX-RS Client 2.1.6 (javax package names), which makes it incompatible with Helidon 3 applications and any application that uses JAX-RS 3 (jakarta package naming). Please see our link:{rootdir}/includes/oci.adoc[Guide] for detailed information on how to work around this issue. + +== Usage + +It is recommended that you use the OCI Java SDK directly, in particular the Async clients. All you need to do is configure and create an OCI SDK Client object. The configuration primarily +consists of setting up authenticate with OCI. + +=== Configuring the OCI SDK Client + +Authentication with OCI is abstracted through `AuthenticationDetailsProvider`. + +If your environment is already set up to work with the OCI SDK or +the OCI command line, then it is very likely you do not need to do any additional +configuration. It is recommended that you do this first, and verify your configuration +by using the link:{oci-javasdk-url}[OCI CLI] to access the service. + +[source,java] +---- +ConfigFile config += ConfigFileReader.parse("~/.oci/config", "DEFAULT"); +AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(config); +---- + +You also need to add the following dependency to your application for this + +[source,xml] +---- + + com.oracle.oci.sdk + oci-java-sdk-common + +---- + +=== Accessing OCI Services + +Once you have authentication with OCI configured, you can use it to access any OCI service +supported by the OCI SDK. You will need to add dependencies for the specific +ODI SDK clients you will use. + +== Examples + +This example describes how to access OCI Object Storage. + +As mentioned above in xref:#_accessing_oci_services[], you need to add a dependency on the OCI SDK +Object Storage API: + +[source,xml] +---- + + com.oracle.oci.sdk + oci-java-sdk-objectstorage + +---- + +=== Creating an Object Storage Client + +Now you can create OCI SDK Clients. + +[source,java] +---- +ConfigFile config += ConfigFileReader.parse("~/.oci/config", "DEFAULT"); +AuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(config); +ObjectStorageAsync objectStorageAsyncClient = new ObjectStorageAsyncClient(authProvider); +---- + +=== Using the Object Storage client + +Once you have created an ObjectStorage client you can use it as described in: + +* link:{oci-javasdk-objstore-javadoc-base-url}/package-summary.html[OCI SDK Object Storage Javadocs] +* link:{oci-objstore-url}[OCI Object Storage Overview] + +== References + +* link:{helidon-github-tree-url}/examples/integrations/oci[OCI SDK Usage Examples] +* link:https://docs.oracle.com/en-us/iaas/Content/home.htm[OCI Documentation] \ No newline at end of file diff --git a/docs/se/oci/atp.adoc b/docs/se/oci/atp.adoc deleted file mode 100644 index 50725711e20..00000000000 --- a/docs/se/oci/atp.adoc +++ /dev/null @@ -1,129 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI Autonomous Transaction Processing -:description: Helidon OCI Autonomous Transaction Processing integration -:keywords: oci, atp -:feature-name: OCI Autonomous Transaction Processing -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -The Helidon SE OCI Autonomous Transaction Processing integration provides a reactive API to ATP database in Oracle cloud. - -== Deprecated - -The custom Helidon SE OCI clients documented here are deprecated. It is recommended that you -use the OCI Java SDK directly, in particular the Async clients. For more information see: - -* link:{oci-database-url}[OCI Database Documentation ] -* link:{oci-javasdk-database-javadoc-base-url}/package-summary.html[OCI Database Javadoc ] -* link:{helidon-github-tree-url}/examples/integrations/oci/atp-reactive/[Helidon SE OCI ATP Example] - -== Experimental - -Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. - APIs and features have not yet been fully tested and are subject to change. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci - helidon-integrations-oci-atp - ----- - -== Setting up the Autonomous Transaction Processing - -In order to use the OCI Autonomous Transaction Processing integration, the following setup should be made: - -[source,java] ----- -Config ociConfig = config.get("oci"); -OciAutonomousDbRx ociAutonomousDb = OciAutonomousDbRx.create(ociConfig); ----- - -Current configuration requires `~/.oci/config` to be available in the home folder. This configuration file can be - downloaded from OCI. - -`Routing` should be added to the `WebServer`, in our case pointing to `/atp`: - -[source,java] ----- -WebServer.builder() - .config(config.get("server")) - .routing(Routing.builder() - .register("/atp", new AtpService(autonomousDbRx, config))) - .build(); ----- - -Additionally, in `application.yaml` OCI properties should be specified: - -[source,yaml] ----- -oci: - atp: - ocid: "" - walletPassword: "" ----- - -The exact values are available from OCI console. - -image::oci/atpocid.png[OCI ATP, align="center"] - -== Using the Autonomous Transaction Processing - -In the Service we must specify the mapping for operations with the database and their handlers: - -[source,java] ----- -@Override -public void update(Routing.Rules rules) { - rules.get("/wallet", this::generateWallet); -} ----- - -=== Generate Wallet - -To generate wallet file for OCI Autonomous Transaction Processing: - -[source,java] ----- -private void generateWallet(ServerRequest req, ServerResponse res) { - autonomousDbRx.generateWallet(GenerateAutonomousDatabaseWallet.Request.builder()) <1> - .flatMapOptional(ApiOptionalResponse::entity) - .map(GenerateAutonomousDatabaseWallet.Response::walletArchive) <2> - .ifEmpty(() -> LOGGER.severe("Unable to obtain wallet!")) - .flatMapSingle(this::createDbClient) <3> - .flatMap(dbClient -> dbClient.execute(exec -> exec.query("SELECT 'Hello world!!' FROM DUAL"))) - .first() - .map(dbRow -> dbRow.column(1).as(String.class)) <4> - .ifEmpty(() -> res.status(404).send()) - .onError(res::send) - .forSingle(res::send); -} ----- -<1> Create the `Request` using `GenerateAutonomousDatabaseWallet.Request.builder()` -<2> Retrieve 'walletArchive' from the response. -<3> Create DBClient using info from 'walletArchive' -<4> Read the first column from first row of result. - -For complete code, about how to create DBClient using wallet info, please see - link:{helidon-github-tree-url}/examples/integrations/oci/atp-reactive[ATP Reactive Example] diff --git a/docs/se/oci/object-storage.adoc b/docs/se/oci/object-storage.adoc deleted file mode 100644 index 7082cf5d029..00000000000 --- a/docs/se/oci/object-storage.adoc +++ /dev/null @@ -1,258 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI Object Storage -:description: Helidon OCI Object Storage integration -:keywords: oci, objectstorage -:feature-name: OCI Object Storage -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -The Helidon SE OCI Object Storage integration provides a reactive API to files stored in Oracle cloud. - -== Deprecated - -The custom Helidon SE OCI clients documented here are deprecated. It is recommended that you -use the OCI Java SDK directly, in particular the Async clients. For more information see: - -* link:{oci-objstore-url}[OCI Object Storage Documentation ] -* link:{oci-javasdk-objstore-javadoc-base-url}/package-summary.html[OCI Object Storage Javadoc ] -* link:{helidon-github-tree-url}/examples/integrations/oci/objectstorage-reactive/[Helidon SE OCI Object Storage Example] - -== Experimental - -Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. APIs and features have not yet been fully tested and are subject to change. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage - ----- - -== Setting up the Object Storage - -In order to use the OCI Object Storage integration, the following setup should be made: - -[source,java] ----- -Config ociConfig = config.get("oci"); -OciObjectStorageRx ociObjectStorage = OciObjectStorageRx.create(ociConfig); ----- - -Current configuration requires `~/.oci/config` to be available in the home folder. - This configuration file can be downloaded from OCI. - -`Routing` should be added to the `WebServer`, in our case pointing to `/file`: - -[source,java] ----- -String bucketName = ociConfig.get("objectstorage").get("bucket").asString().get(); - -WebServer.builder() - .config(config.get("server")) - .routing(Routing.builder() - .register("/files", new ObjectStorageService(ociObjectStorage, bucketName))) - .build() - .start() - .await() ----- - -Additionally, in `application.yaml` OCI properties should be specified: - -[source,yaml] ----- -oci: - properties: - compartment-ocid: "ocid<1>tenancy.oc<1>.<..>" - objectstorage-namespace: "<...>" - objectstorage-bucket: "<...>" ----- - -The exact values are available in OCI object storage and bucket properties. - -image::oci/ocibucket.png[OCI Bucket, align="center"] - -== Using the Object Storage - -In the Service we must specify the mapping for CRUD operations with the files and their handlers: - -[source,java] ----- -@Override -public void update(Routing.Rules rules) { - rules.get("/file/{file-name}", this::download) - .post("/file/{file-name}", this::upload) - .delete("/file/{file-name}", this::delete) - .get("/rename/{old-name}/{new-name}", this::rename); -} ----- - -=== Upload file - -To upload a file to OCI Object Storage using the `PUT` method: - -[source,java] ----- -private void upload(ServerRequest req, ServerResponse res) { - OptionalLong contentLength = req.headers().contentLength(); - if (contentLength.isEmpty()) { - req.content().forEach(DataChunk::release); - res.status(Http.Status.BAD_REQUEST_400).send("Content length must be defined"); - return; - } - - String objectName = req.path().param("file-name"); - - PutObject.Request request = PutObject.Request.builder() // <1> - .objectName(objectName) - .bucket(bucketName) - .contentLength(contentLength.getAsLong()); - - req.headers().contentType().ifPresent(request::requestMediaType); // <2> - - objectStorage.putObject(request, - req.content()) - .forSingle(response -> res.send(response.requestId())) // <3> - .exceptionally(res::send); -} ----- -<1> Create the `Request` using `PutObject.Request.builder()` -<2> Define `MediaType` -<3> Execute the request to OCI in asynchronous way and put the result in `response` object - -=== Download file - -To download a file from OCI Object Storage using the `GET` method: - -[source,java] ----- -private void download(ServerRequest req, ServerResponse res) { - String objectName = req.path().param("file-name"); - - objectStorage.getObject(GetObject.Request.builder() - .bucket(bucketName) - .objectName(objectName)) // <1> - .forSingle(apiResponse -> { - Optional entity = apiResponse.entity(); // <2> - if (entity.isEmpty()) { - res.status(Http.Status.NOT_FOUND_404).send(); // <3> - } else { - GetObjectRx.Response response = entity.get(); - // copy the content length header to response - apiResponse.headers() - .first(Http.Header.CONTENT_LENGTH) - .ifPresent(res.headers()::add); - res.send(response.publisher()); // <4> - } - }) - .exceptionally(res::send); -} ----- -<1> Use `getObject` function to make asynchronous request to OCI Object Storage -<2> The result is of type `Optional` -<3> Whenever the result is empty, return status `404` -<4> Get the response, set headers and return the result as a `Publisher` - -=== Rename file - -To rename an existing file in the OCI bucket, submit a `GET` method with two parameters: - -[source,java] ----- -private void rename(ServerRequest req, ServerResponse res) { - String oldName = req.path().param("old-name"); - String newName = req.path().param("new-name"); - - objectStorage.renameObject(RenameObject.Request.builder() - .bucket(bucketName) - .objectName(oldName) - .newObjectName(newName)) // <1> - .forSingle(it -> res.send("Renamed to " + newName)) // <2> - .exceptionally(res::send); -} ----- -<1> Use `renameObject` function and configure a `RenameObject.Request.builder()` to submit the rename request -<2> The request is made in asynchronous way; a `Single` is returned - - -=== Delete file - -Finally, to delete a file, `DELETE` request should be used: - -[source,java] ----- -private void delete(ServerRequest req, ServerResponse res) { - String objectName = req.path().param("file-name"); - - objectStorage.deleteObject(DeleteObject.Request.builder() - .bucket(bucketName) - .objectName(objectName)) // <1> - .forSingle(response -> res.status(response.status()).send()) // <2> - .exceptionally(res::send); -} ----- -<1> Use `deleteObject` function and configure a `DeleteObject.Request.builder()` to submit the delete request -<2> The request is made in asynchronous way; a `Single` is returned - -== Object Storage Health Check - -If your Helidon application depends on Object Storage accessibility, you may consider setting -up a health check to verify connectivity with an OCI bucket. To do so, first add the following -dependency in your pom file: - -[source,xml] ----- - - io.helidon.integrations.oci - helidon-integrations-oci-objectstorage-health - ----- - -In order to register the new health check in Helidon SE, create an instance of `HealthSupport` -and configure it as shown next: - -[source,java] ----- -HealthSupport health = HealthSupport.builder() - .addLiveness(OciObjectStorageHealthCheck.builder() - .ociObjectStorage(ociObjectStorage) - .bucket(bucketName) - .namespace(namespace) - .build()) - .build(); ----- - -where `ociObjectStorage`, `bucketName` and `namespace` are as required for any other -Object Storage access. Finally, include your newly created `HealthSupport` object -as part of your application's routing: - -[source,java] ----- -Routing routing = Routing.builder() - .register(health) - // other routes here - .build(); ----- - -When executed, this health check will _ping_ the bucket to make sure it is accessible in your -environment. For more information about health checks see xref:../health.adoc[Health Checks]. diff --git a/docs/se/oci/oci.adoc b/docs/se/oci/oci.adoc deleted file mode 100644 index 139258e8f26..00000000000 --- a/docs/se/oci/oci.adoc +++ /dev/null @@ -1,84 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Oracle Cloud Infrastructure Integration -:description: Helidon OCI Integration -:keywords: oci -:feature-name: OCI Integration -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Helidon SE OCI Integration provides easy access to Oracle Cloud Infrastructure. - -== Deprecated - -The custom Helidon SE OCI clients documented here are deprecated. -It is recommended that you use the OCI Java SDK directly, in particular the Async clients. -For more information see: - -* link:https://docs.oracle.com/en-us/iaas/Content/home.htm[OCI Documentation ] -* link:{helidon-tag}/examples/integrations/oci/atp-reactive/[Helidon SE OCI ATP Example] -* link:{helidon-tag}/examples/integrations/oci/objectstorage-reactive/[Helidon SE OCI Object Storage Example] -* link:{helidon-tag}/examples/integrations/oci/metrics-reactive/[Helidon SE OCI Metrics Example] -* link:{helidon-github-tree-url}/examples/integrations/oci/vault-reactive/[Helidon SE OCI Vault Example] - -== Experimental - -Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. - APIs and features have not yet been fully tested and are subject to change. - -== General Configuration - -NOTE: If you follow these instructions on how to -https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#two[Generate an API Signing Key], -be advised that Helidon does not currently support passphrase-protected private keys in PKCS#1 format. -If generating a private key using those instructions, use the _no passphrase_ option. - -=== Using Helidon SE Properties Configuration - -The first option to configure connection to OCI is to directly specify properties in `application.yaml` file: - -[source,yaml] ----- -oci: - config: - oci-profile: - user: ocid1.user.... - fingerprint: 1c:6c:.... - tenancy: ocid1.tenancy.oc1.. - region: us-... - key-pem: ----- - -=== Using OCI Configuration - -The second option is via OCI configuration file. -For authentication in OCI a special configuration file should be set up. The file is usually located at `~/.oci/config` - -[source,listing] ----- -[DEFAULT] -user=ocid1.user.... -fingerprint=1c:6c:.... -tenancy=ocid1.tenancy.oc1.. -region=us-... -key_file= ----- - -More information how to set up on your environment: link:{oci-sdk-config-url}[Official website] diff --git a/docs/se/oci/vault.adoc b/docs/se/oci/vault.adoc deleted file mode 100644 index a3953fda19c..00000000000 --- a/docs/se/oci/vault.adoc +++ /dev/null @@ -1,260 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2021, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= OCI Vault -:description: Helidon OCI Vault integration -:keywords: oci, vault -:feature-name: OCI Vault -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -The Helidon SE OCI Vault integration provides a reactive API for the Oracle Cloud Vault service. - -== Deprecated - -The custom Helidon SE OCI clients documented here are deprecated. It is recommended that you -use the OCI Java SDK directly, in particular the Async clients. For more information see: - -* link:{oci-vault-url}[OCI Vault Storage Documentation ] -* link:{oci-javasdk-vault-javadoc-base-url}/package-summary.html[OCI Vault Storage Javadoc ] -* link:{helidon-github-tree-url}/examples/integrations/oci/vault-reactive/[Helidon SE OCI Vault Example] - -== Experimental - -Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. -APIs and features have not yet been fully tested and are subject to change. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.oci - helidon-integrations-oci-vault - ----- - - -== Setting up the OCI Vault - -In order to use the OCI Vault integration, the following setup should be made. - -* The configuration required for Vault integration includes: -* Vault OCID - to use the correct Vault, as more than one can be configured -* Compartment OCID - OCI-specific compartment -* Encryption Key OCID - required when doing encryption/decryption -* Signature Key OCID - required when doing signatures/verification -* Cryptographic endpoint - required for all except secrets - -First specify OCIDs and URLs of Vault items in `application.yaml`: - -[source,yaml] ----- -oci: - vault: - vault-ocid: "<...>" - compartment-ocid: "<...>" - encryption-key-ocid: "<...>" - signature-key-ocid: "<...>" - cryptographic-endpoint: "<...>" ----- -Current configuration requires `~/.oci/config` to be available in the home folder. -This configuration file can be downloaded from OCI. - -The OCIDs can be set up and found in OCI under Security tab. - -image::oci/vaultkey.png[OCI Vault, align="center"] - -Next, these values should be read and provided to `VaultService`: - -[source,java] ----- -Config vaultConfig = config.get("oci.vault"); -// the following three parameters are required -String vaultOcid = vaultConfig.get("vault-ocid").asString().get(); -String compartmentOcid = vaultConfig.get("compartment-ocid").asString().get(); -String encryptionKey = vaultConfig.get("encryption-key-ocid").asString().get(); -String signatureKey = vaultConfig.get("signature-key-ocid").asString().get(); - -// this requires OCI configuration in the usual place -// ~/.oci/config -OciVaultRx ociVault = OciVaultRx.create(config.get("oci")); - -WebServer.builder() - .config(config.get("server")) - .routing(Routing.builder() - .register("/vault", new VaultService(ociVault, - vaultOcid, - compartmentOcid, - encryptionKey, - signatureKey))) - .build() - .start() - .await(); ----- - -The `VaultService` should define an `update` method to map paths to handler methods: - -[source,java] ----- -@Override -public void update(Routing.Rules rules) { - rules.get("/encrypt/{text:.*}", this::encrypt) - .get("/decrypt/{text:.*}", this::decrypt) - .get("/sign/{text}", this::sign) - .get("/verify/{text}/{signature:.*}", this::verify) - .get("/secret/{id}", this::getSecret) - .post("/secret/{name}", Handler.create(String.class, this::createSecret)) - .delete("/secret/{id}", this::deleteSecret); -} ----- - -== OCI Vault usage - -=== Encryption - -To encrypt a text, submit a `GET` request to the `/encrypt` endpoint: - -[source,java] ----- -private void encrypt(ServerRequest req, ServerResponse res) { - vault.encrypt(Encrypt.Request.builder() - .keyId(encryptionKeyOcid) - .data(Base64Value.create(req.path().param("text")))) - .map(Encrypt.Response::cipherText) - .forSingle(res::send) - .exceptionally(res::send); -} ----- - -=== Decryption - -To decrypt a text, submit a `GET` request to `/decrypt` endpoint: - -[source,java] ----- -private void decrypt(ServerRequest req, ServerResponse res) { - vault.decrypt(Decrypt.Request.builder() - .keyId(encryptionKeyOcid) - .cipherText(req.path().param("text"))) - .map(Decrypt.Response::decrypted) - .map(Base64Value::toDecodedString) - .forSingle(res::send) - .exceptionally(res::send); -} ----- - -=== Signature - -To retrieve a signature, submit a `GET` request to `/sign` endpoint: - -[source,java] ----- -private void sign(ServerRequest req, ServerResponse res) { - vault.sign(Sign.Request.builder() - .keyId(signatureKeyOcid) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .message(Base64Value.create(req.path().param("text")))) - .map(Sign.Response::signature) - .map(Base64Value::toBase64) - .forSingle(res::send) - .exceptionally(res::send); -} ----- - -==== Verification of a Signature - -To verify the correctness of the signature, submit a `GET` request to `/verify` endpoint: - -[source,java] ----- -private void verify(ServerRequest req, ServerResponse res) { - String text = req.path().param("text"); - String signature = req.path().param("signature"); - - vault.verify(Verify.Request.builder() - .keyId(signatureKeyOcid) - .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS) - .message(Base64Value.create(text)) - .signature(Base64Value.createFromEncoded(signature))) - .map(Verify.Response::isValid) - .map(it -> it ? "Signature Valid" : "Signature Invalid") - .forSingle(res::send) - .exceptionally(res::send); -} ----- - -==== Creating a Signature - -To create a secret with a provided name, submit a `GET` request to `/secret`: - -[source,java] ----- -private void createSecret(ServerRequest req, ServerResponse res, String secretText) { - vault.createSecret(CreateSecret.Request.builder() - .secretContent(CreateSecret.SecretContent.create(secretText)) - .vaultId(vaultOcid) - .compartmentId(compartmentOcid) - .encryptionKeyId(encryptionKeyOcid) - .secretName(req.path().param("name"))) - .map(CreateSecret.Response::secret) - .map(Secret::id) - .forSingle(res::send) - .exceptionally(res::send); -} ----- - -==== Getting a Signature - -To get a secret by its OCID, submit a `GET` request to `/secret`: - -[source,java] ----- -private void getSecret(ServerRequest req, ServerResponse res) { - vault.getSecretBundle(GetSecretBundle.Request.create(req.path().param("id"))) - .forSingle(apiResponse -> { - Optional entity = apiResponse.entity(); - if (entity.isEmpty()) { - res.status(Http.Status.NOT_FOUND_404).send(); - } else { - GetSecretBundle.Response response = entity.get(); - res.send(response.secretString().orElse("")); - } - }) - .exceptionally(res::send); -} ----- - -==== Deleting a Signature - -To delete a secret, a `DELETE` request to `/secret` should be used: - -[source, java] ----- -private void deleteSecret(ServerRequest req, ServerResponse res) { - Instant deleteTime = Instant.now().plus(30, ChronoUnit.DAYS); - - vault.deleteSecret(DeleteSecret.Request.builder() - .secretId(req.path().param("id")) - .timeOfDeletion(deleteTime)) - .forSingle(it -> res.status(it.status()).send()) - .exceptionally(res::send); - -} ----- diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 48fd3894b6b..567d1d9463c 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -180,16 +180,14 @@ backend: type: "icon" value: "donut_large" - type: "MENU" - title: "OCI" - dir: "oci" + title: "Integrations" + dir: "integrations" glyph: type: "icon" value: "filter_drama" sources: - "oci.adoc" - - "object-storage.adoc" - - "vault.adoc" - - "atp.adoc" + - "hcv.adoc" - type: "MENU" title: "Reactive Streams" dir: "reactivestreams" @@ -241,12 +239,6 @@ backend: glyph: type: "icon" value: "timeline" - - type: "PAGE" - title: "Vault" - source: "vault.adoc" - glyph: - type: "icon" - value: "lock" - type: "MENU" title: "Web Client" dir: "webclient" @@ -424,16 +416,14 @@ backend: type: "icon" value: "donut_large" - type: "MENU" - title: "OCI" - dir: "oci" + title: "Integrations" + dir: "integrations" glyph: type: "icon" value: "filter_drama" sources: - "oci.adoc" - - "object-storage.adoc" - - "vault.adoc" - - "atp.adoc" + - "hcv.adoc" - type: "MENU" title: "Reactive Streams" dir: "reactivestreams" @@ -483,12 +473,6 @@ backend: glyph: type: "icon" value: "timeline" - - type: "PAGE" - title: "Vault" - source: "vault.adoc" - glyph: - type: "icon" - value: "lock" - type: "PAGE" title: "Websocket" source: "websocket.adoc" diff --git a/examples/integrations/oci/README.md b/examples/integrations/oci/README.md new file mode 100644 index 00000000000..cfa76cf3d60 --- /dev/null +++ b/examples/integrations/oci/README.md @@ -0,0 +1,11 @@ +# OCI SDK setup for Examples Build + +OCI SDK uses JAX-RS Client 2.1.6 (javax package names), which makes it incompatible with Helidon 3 applications and any application that uses JAX-RS 3 (jakarta package naming). + +Please see our [Guide](https://github.com/oracle/helidon/tree/master/docs/includes/oci.adoc) for detailed information on how to work around this issue. + +Once you have this setup, you can build examples in this repository directory. + +Please make sure you supply -Poci-sdk-cdi to your mvn invocation. + +If you do not supply that profile, then the examples will not be built. \ No newline at end of file From 9f0fd2360a7822d4a5512fb55381c12948eeaa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Tue, 19 Jul 2022 12:00:44 +0200 Subject: [PATCH 40/51] WebClient documentation (#4517) WebClient documentation Signed-off-by: David Kral --- docs/about/introduction.adoc | 2 +- docs/config/config_reference.adoc | 3 + .../io_helidon_media_common_MediaContext.adoc | 41 +++- docs/config/io_helidon_webclient_Proxy.adoc | 66 ++++++ ...idon_webclient_WebClientConfiguration.adoc | 68 ++++++ .../io_helidon_webclient_WebClientTls.adoc | 53 +++++ docs/se/introduction.adoc | 2 +- .../introduction.adoc => webclient.adoc} | 205 +++++++++++++----- docs/se/webclient/tls-configuration.adoc | 116 ---------- docs/sitegen.yaml | 7 +- media/common/pom.xml | 12 + .../io/helidon/media/common/MediaContext.java | 17 +- media/common/src/main/java/module-info.java | 4 +- webclient/webclient/pom.xml | 12 + .../main/java/io/helidon/webclient/Proxy.java | 12 +- .../webclient/WebClientConfiguration.java | 29 ++- .../io/helidon/webclient/WebClientTls.java | 20 +- .../webclient/src/main/java/module-info.java | 2 + 18 files changed, 476 insertions(+), 195 deletions(-) create mode 100644 docs/config/io_helidon_webclient_Proxy.adoc create mode 100644 docs/config/io_helidon_webclient_WebClientConfiguration.adoc create mode 100644 docs/config/io_helidon_webclient_WebClientTls.adoc rename docs/se/{webclient/introduction.adoc => webclient.adoc} (71%) delete mode 100644 docs/se/webclient/tls-configuration.adoc diff --git a/docs/about/introduction.adoc b/docs/about/introduction.adoc index 8dc11812062..c53280914ac 100644 --- a/docs/about/introduction.adoc +++ b/docs/about/introduction.adoc @@ -88,7 +88,7 @@ MP Reactive Operators will be included in both frameworks, while MP Reactive Mes * *Helidon Web Client* + The new reactive web client can integrate with other Helidon SE APIs. -xref:../se/webclient/introduction.adoc[Learn more about the Helidon Web Client]. +xref:../se/webclient.adoc[Learn more about the Helidon Web Client]. * *Additional Websocket Support* + Based upon the Tyrus implementation, Helidon receives WebSocket API support. diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index 02c45cdf6ea..7d3e7b08034 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -63,6 +63,7 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig (security.providers.common)] - xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget (security.providers.common)] - xref:{rootdir}/config/io_helidon_common_pki_KeyConfig_PemBuilder.adoc[PemBuilder (common.pki.KeyConfig)] +- xref:{rootdir}/config/io_helidon_webclient_Proxy.adoc[Proxy (webclient)] - xref:{rootdir}/config/io_helidon_metrics_api_RegistryFilterSettings.adoc[RegistryFilterSettings (metrics.api)] - xref:{rootdir}/config/io_helidon_metrics_api_RegistrySettings.adoc[RegistrySettings (metrics.api)] - xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource (common.configurable)] @@ -78,6 +79,8 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_faulttolerance_Timeout.adoc[Timeout (faulttolerance)] - xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler (security.util)] - xref:{rootdir}/config/io_helidon_tracing_TracerBuilder.adoc[TracerBuilder (tracing)] +- xref:{rootdir}/config/io_helidon_webclient_WebClientConfiguration.adoc[WebClientConfiguration (webclient)] +- xref:{rootdir}/config/io_helidon_webclient_WebClientTls.adoc[WebClientTls (webclient)] - xref:{rootdir}/config/io_helidon_webserver_WebServer.adoc[WebServer (webserver)] - xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls (webserver)] - xref:{rootdir}/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc[ZipkinTracer (tracing.zipkin)] diff --git a/docs/config/io_helidon_media_common_MediaContext.adoc b/docs/config/io_helidon_media_common_MediaContext.adoc index cf6cabfbad2..ded418e5a42 100644 --- a/docs/config/io_helidon_media_common_MediaContext.adoc +++ b/docs/config/io_helidon_media_common_MediaContext.adoc @@ -26,6 +26,7 @@ include::{rootdir}/includes/attributes.adoc[] // tag::config[] + Type: link:{javadoc-base-url}/io.helidon.media.common/io/helidon/media/common/MediaContext.html[io.helidon.media.common.MediaContext] @@ -33,6 +34,39 @@ Type: link:{javadoc-base-url}/io.helidon.media.common/io/helidon/media/common/Me == Configuration options +Required configuration options: +[cols="3,3,2,5a"] +|=== +|key |type |default value |description + +|`services` |io.helidon.media.common.spi.MediaSupportProvider[] (service provider interface) |{nbsp} |Configures this Builder from the supplied Config. + + + + + + + + + + + + + + + + + + + + + + +
Optional configuration parameters
keydescription
register-defaultsWhether to register default reader and writers
discover-servicesWhether to discover services via service loader
filter-servicesWhether to filter discovered services by service names in services section
servicesConfiguration section for each service. Each entry has to have "name" parameter. + It is also used for filtering of loaded services.
+ +|=== + Optional configuration options: @@ -41,11 +75,10 @@ Optional configuration options: |=== |key |type |default value |description -|`discover-services` |boolean |{nbsp} |Whether Java Service Loader should be used to load MediaSupportProvider. -|`register-defaults` |boolean |{nbsp} |Whether default readers and writers should be registered -|`filter-services` |boolean |{nbsp} |Whether services loaded by Java Service Loader should be filtered. +|`discover-services` |boolean |`false` |Whether Java Service Loader should be used to load MediaSupportProvider. +|`filter-services` |boolean |`false` |Whether services loaded by Java Service Loader should be filtered. All of the services which should pass the filter, have to be present under `services` section of configuration. -|`services` |Object[] |{nbsp} |Configuration section for each service. Each entry has to have "name" parameter. It is also used for filtering of loaded services +|`register-defaults` |boolean |`true` |Whether default readers and writers should be registered. |=== diff --git a/docs/config/io_helidon_webclient_Proxy.adoc b/docs/config/io_helidon_webclient_Proxy.adoc new file mode 100644 index 00000000000..16a611ed9c5 --- /dev/null +++ b/docs/config/io_helidon_webclient_Proxy.adoc @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webclient.Proxy +:keywords: helidon, config, io.helidon.webclient.Proxy +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webclient.Proxy +include::{rootdir}/includes/attributes.adoc[] + += Proxy (webclient) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.webclient/io/helidon/webclient/Proxy.html[io.helidon.webclient.Proxy] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`host` |string |{nbsp} |Sets a new host value. +|`no-proxy` |string[] |{nbsp} |Configure a host pattern that is not going through a proxy. + + Options are: + +- IP Address, such as `192.168.1.1` +- IP V6 Address, such as `[2001:db8:85a3:8d3:1319:8a2e:370:7348]` +- Hostname, such as `localhost` +- Domain name, such as `helidon.io` +- Domain name and all sub-domains, such as `.helidon.io` (leading dot) +- Combination of all options from above with a port, such as `.helidon.io:80` + + +|`password` |string |{nbsp} |Sets a new password for the proxy. +|`port` |int |{nbsp} |Sets a port value. +|`type` |ProxyType (NONE, SYSTEM, HTTP, SOCKS_4, SOCKS_5) |`HTTP` |Sets a new proxy type. +|`use-system-selector` |boolean |`false` |Configure proxy from environment variables and system properties. +|`username` |string |{nbsp} |Sets a new username for the proxy. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_webclient_WebClientConfiguration.adoc b/docs/config/io_helidon_webclient_WebClientConfiguration.adoc new file mode 100644 index 00000000000..aa77f12efc0 --- /dev/null +++ b/docs/config/io_helidon_webclient_WebClientConfiguration.adoc @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webclient.WebClientConfiguration +:keywords: helidon, config, io.helidon.webclient.WebClientConfiguration +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webclient.WebClientConfiguration +include::{rootdir}/includes/attributes.adoc[] + += WebClientConfiguration (webclient) Configuration + +// tag::config[] + +Configuration of the HTTP client + + +Type: link:{javadoc-base-url}/io.helidon.webclient/io/helidon/webclient/WebClientConfiguration.html[io.helidon.webclient.WebClientConfiguration] + + +This is a standalone configuration type, prefix from configuration root: `client` + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`connect-timeout-millis` |long |`60000` |Sets new connection timeout of the request. +|`cookies.automatic-store-enabled` |boolean |{nbsp} |Whether to allow automatic cookie storing +|`cookies.default-cookies` |Map |{nbsp} |Default cookies to be used in each request. Each list entry has to have "name" and "value" node +|`follow-redirects` |boolean |`false` |Whether to follow any response redirections or not. +|`headers` |Map |{nbsp} |Default headers to be used in each request. Each list entry has to have "name" and "value" node +|`max-redirects` |int |`5` |Sets max number of followed redirects. +|`media-support` |xref:{rootdir}/config/io_helidon_media_common_MediaContext.adoc[MediaContext] |{nbsp} | +|`proxy` |xref:{rootdir}/config/io_helidon_webclient_Proxy.adoc[Proxy] |{nbsp} |Sets new request proxy. +|`read-timeout-millis` |long |`600000` |Sets new read timeout of the response. +|`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. +|`tls` |xref:{rootdir}/config/io_helidon_webclient_WebClientTls.adoc[WebClientTls] |{nbsp} |New TLS configuration. +|`uri` |string |{nbsp} |Base uri for each request. + + @return updated builder instance +|`user-agent` |string |{nbsp} |Name of the user agent which should be used. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_webclient_WebClientTls.adoc b/docs/config/io_helidon_webclient_WebClientTls.adoc new file mode 100644 index 00000000000..6c2314a5a34 --- /dev/null +++ b/docs/config/io_helidon_webclient_WebClientTls.adoc @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webclient.WebClientTls +:keywords: helidon, config, io.helidon.webclient.WebClientTls +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webclient.WebClientTls +include::{rootdir}/includes/attributes.adoc[] + += WebClientTls (webclient) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.webclient/io/helidon/webclient/WebClientTls.html[io.helidon.webclient.WebClientTls] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`client.keystore` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig] |{nbsp} |Client key store which contains client private key and certificate +|`server.cipher-suite` |string[] |{nbsp} |List of allowed ciphers. If set, replaces those present by default +|`server.disable-hostname-verification` |boolean |`false` |Whether this client should perform hostname verification +|`server.trust-all` |boolean |`false` |Whether this client should trust all certificates +|`server.truststore` |xref:{rootdir}/config/io_helidon_common_pki_KeyConfig.adoc[KeyConfig] |{nbsp} |Trust store which contains trusted certificates. If set, replaces those present by default + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index 00c771677c5..b8974b4373b 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -134,7 +134,7 @@ Profile and monitor your applications across multiple services. //WebClient [CARD] .WebClient -[icon=http,link=webclient/introduction.adoc] +[icon=http,link=webclient.adoc] -- HTTP client that handles responses to the HTTP requests in a reactive way. -- diff --git a/docs/se/webclient/introduction.adoc b/docs/se/webclient.adoc similarity index 71% rename from docs/se/webclient/introduction.adoc rename to docs/se/webclient.adoc index 81e6d1e3680..61304b5c1eb 100644 --- a/docs/se/webclient/introduction.adoc +++ b/docs/se/webclient.adoc @@ -20,22 +20,22 @@ :description: Helidon WebClient :keywords: helidon, se, rest, httpclient, webclient, reactive :feature-name: WebClient -:rootdir: {docdir}/../.. +:rootdir: {docdir}/.. include::{rootdir}/includes/se.adoc[] -include::{rootdir}/includes/dependencies.adoc[] -[source,xml] ----- - - io.helidon.webclient - helidon-webclient - ----- +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> == Overview -WebClient is an HTTP client for Helidon SE 2.0. It handles the responses to the HTTP requests in a reactive way. +WebClient is an HTTP client of Helidon SE. It handles the responses to the HTTP requests in a reactive way. Helidon WebClient provides the following features: @@ -51,11 +51,67 @@ Follows the redirect chain and perform requests on the correct endpoint by itsel * *Tracing, metrics and security propagation* + Automatically propagates the configured tracing, metrics and security settings of the Helidon WebServer to the WebClient and uses them during request and response. +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.webclient + helidon-webclient + +---- + +== Usage + +=== Creating the WebClient + +You can create WebClient by executing `WebClient.create()` method. This will create an instance of client with default settings and without a base uri set. + +To change the default settings and register +additional services, you can use simple builder that allows you to customize the client behavior. + +.Create a WebClient with simple builder: +[source,java] +---- +WebClient client = WebClient.builder() + .baseUri("http://localhost") + .build(); +---- + +=== Creating and Executing the WebClient Request + +WebClient executes requests to the target endpoints and returns specific response type. + +It offers variety of methods to specify the type of request you want to execute: + +* `put()` +* `get()` +* `method(String methodName)` + +These methods set specific request type based on their name or parameter to the new instance of `WebClientRequesBuilder` and return this instance based on configurations for specific request type. + +You can set configuration for every request type before it is sent as described in <>. + +For the final execution, use the following methods with variations and different parameters: + +* `Single submit(Object entity, Class responseType)` +* `Single request(Class responseType)` + +.Execute a simple GET request to endpoint: +[source,java] +---- +Single response = client.get() + .path("/endpoint") + .request(String.class); +---- + == Configuring the WebClient -The WebClient default configuration may be suitable in most use cases. However, you can configure it to suit your specific requirements. +The class responsible for WebClient configuration is: + +include::{rootdir}/config/io_helidon_webclient_WebClientConfiguration.adoc[leveloffset=+1,tag=config] -=== Example of a WebClient Configuration +=== Example of a WebClient Runtime Configuration [source,java] ---- @@ -66,7 +122,7 @@ WebClient client = WebClient.builder() .build(); ---- -=== Example of Yaml WebClient Configuration +=== Example of a WebClient YAML Configuration [source, java] ---- @@ -134,49 +190,7 @@ client: <4> Proxy configuration <5> TLS configuration -== Creating the WebClient - -You can create WebClient by executing `WebClient.create()` method. This will create an instance of client with default settings and without a base uri set. - -To change the default settings and register -additional services, you can use simple builder that allows you to customize the client behavior. - -=== Example -.Create a WebClient with simple builder: -[source,java] ----- -WebClient client = WebClient.builder() - .baseUri("http://localhost") - .build(); ----- - -== Creating and Executing the WebClient Request - -WebClient executes requests to the target endpoints and returns specific response type. - -It offers variety of methods to specify the type of request you want to execute: - -* `put()` -* `get()` -* `method(String methodName)` - -These methods set specific request type based on their name or parameter to the new instance of `WebClientRequesBuilder` and return this instance based on configurations for specific request type. - -You can set configuration for every request type before it is sent as described in <>. - -For the final execution, use the following methods with variations and different parameters: - -* `Single submit(Object entity, Class responseType)` -* `Single request(Class responseType)` - -=== Example -.Execute a simple GET request to endpoint: -[source,java] ----- -Single response = client.get() - .path("/endpoint") - .request(String.class); ----- +== Examples === Request Configuration @@ -199,13 +213,11 @@ The request settings are based on the following optional parameters, and change For more details, see the link:{webserver-javadoc-base-url}/io/helidon/webserver/RequestHeaders.html[Request Headers] API. -== Adding JSON Processing Media Support to the WebClient +=== Adding JSON Processing Media Support to the WebClient JSON Processing (JSON-P) media support is not present in the WebClient by default. So, in this case, you must first register it before making a request. This example shows how to register `JsonpSupport` using the following two methods. -=== Example - [source,java] .Register JSON-P support to the WebClient. ---- @@ -233,4 +245,77 @@ requestBuilder.readerContext().registerReader(JsonSupport.reader()); // <2> requestBuilder.request(JsonObject.class) ---- <1> Adds JSON-P writer only to this request. -<2> Adds JSON-P reader only to this request. \ No newline at end of file +<2> Adds JSON-P reader only to this request. + +=== WebClient TLS setup + +Configure TLS either programmatically or by the Helidon configuration framework. + +==== Configuring TLS in your code + +One way to configure TLS in WebClient is in your application code as shown below. + +[source,java] +---- +KeyConfig keyConfig = KeyConfig.keystoreBuilder() + //Whether this keystore is also trust store + .trustStore() + //Keystore location/name + .keystore(Resource.create("client.p12")) + //Password to the keystore + .keystorePassphrase("password") + .build(); + +WebClient.builder() + .tls(WebClientTls.builder() + .certificateTrustStore(keyConfig) + .clientKeyStore(keyConfig) + .build()) + .build(); +---- + +==== Configuring TLS in the config file + +Another way to configure TLS in WebClient is through the `application.yaml` configuration file. + +[source,yaml] +.WebClient TLS configuration file `application.yaml` +---- +webclient: + tls: + #Server part defines settings for server certificate validation and truststore + server: + keystore: + passphrase: "password" + trust-store: true + resource: + resource-path: "keystore.p12" + #Client part defines access to the keystore with client private key or certificate + client: + keystore: + passphrase: "password" + resource: + resource-path: "keystore.p12" +---- +Then, in your application code, load the configuration from that file. + +[source,java] +.WebClient initialization using the `application.yaml` file located on the classpath +---- +Config config = Config.create(); +WebClient webClient = WebClient.create(config.get("webclient")); +---- +Or you can only create WebClientTls instance based on the config file. + +[source,java] +.WebClientTls instance based on `application.yaml` file located on the classpath +---- +Config config = Config.create(); +WebClientTls.builder() + .config(config.get("webclient.tls")) + .build(); +---- + +== Reference + +* link:https://helidon.io/docs/v2/apidocs/io.helidon.webclient/module-summary.html[Helidon WebClient JavaDoc] diff --git a/docs/se/webclient/tls-configuration.adoc b/docs/se/webclient/tls-configuration.adoc deleted file mode 100644 index ebbc647a417..00000000000 --- a/docs/se/webclient/tls-configuration.adoc +++ /dev/null @@ -1,116 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= WebClient TLS configuration -:description: Helidon WebClient TLS configuration -:keywords: helidon, se, rest, httpclient, webclient, reactive, tls -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Configure TLS either programmatically or by the Helidon configuration framework. - -== Configuring TLS in your code - -The one way to configure TLS in WebClient is in your application code. - -[source,java] ----- -KeyConfig keyConfig = KeyConfig.keystoreBuilder() - //Whether this keystore is also trust store - .trustStore() - //Keystore location/name - .keystore(Resource.create("client.p12")) - //Password to the keystore - .keystorePassphrase("password") - .build(); - -WebClient.builder() - .tls(WebClientTls.builder() - .certificateTrustStore(keyConfig) - .clientKeyStore(keyConfig) - .build()) - .build(); ----- - -== Configuring TLS in the config file - -It is also possible to configure TLS via the config file. - -[source,yaml] -.WebClient TLS configuration file `application.yaml` ----- -webclient: - tls: - #Server part defines settings for server certificate validation and truststore - server: - keystore: - passphrase: "password" - trust-store: true - resource: - resource-path: "keystore.p12" - #Client part defines access to the keystore with client private key or certificate - client: - keystore: - passphrase: "password" - resource: - resource-path: "keystore.p12" ----- -Then, in your application code, load the configuration from that file. - -[source,java] -.WebClient initialization using the `application.yaml` file located on the classpath ----- -Config config = Config.create(); -WebClient webClient = WebClient.create(config.get("webclient")); ----- -Or you can only create WebClientTls instance based on the config file. - -[source,java] -.WebClientTls instance based on `application.yaml` file located on the classpath ----- -Config config = Config.create(); -WebClientTls.builder() - .config(config.get("webclient.tls")) - .build(); ----- - -== Configuration options - -See all configuration options -link:{webclient-javadoc-base-url}/io/helidon/webclient/WebClientTls.html[here]. - -Available server certificate configuration options: - -[cols="^2s,<2,<2,<6"] -|=== -|Configuration key |Default value ^|Java type ^|Description - -|`disable-hostname-verification` |false |boolean |Whether hostname verification should be performed -|`trust-all` |false |boolean |Whether all of the server certificates should be trusted -|`keystore` |{nbsp} |Object |Keystore configuration, please follow the example above -|=== - -Available client configuration options: - -[cols="^2s,<2,<2,<6"] -|=== -|Configuration key |Default value ^|Java type ^|Description - -|`keystore` |{nbsp} |Object |Keystore configuration, please follow the example above -|=== \ No newline at end of file diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 567d1d9463c..bbfac482aec 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -239,15 +239,12 @@ backend: glyph: type: "icon" value: "timeline" - - type: "MENU" + - type: "PAGE" title: "Web Client" - dir: "webclient" + source: "webclient.adoc" glyph: type: "icon" value: "http" - sources: - - "introduction.adoc" - - "tls-configuration.adoc" - type: "PAGE" title: "Websocket" source: "websocket.adoc" diff --git a/media/common/pom.xml b/media/common/pom.xml index 14f84c8ca80..5b3f3cea905 100644 --- a/media/common/pom.xml +++ b/media/common/pom.xml @@ -59,5 +59,17 @@ hamcrest-all test + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true +
diff --git a/media/common/src/main/java/io/helidon/media/common/MediaContext.java b/media/common/src/main/java/io/helidon/media/common/MediaContext.java index 188dd12de5d..ad9034a8931 100644 --- a/media/common/src/main/java/io/helidon/media/common/MediaContext.java +++ b/media/common/src/main/java/io/helidon/media/common/MediaContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -27,6 +27,8 @@ import io.helidon.common.serviceloader.HelidonServiceLoader; import io.helidon.config.Config; import io.helidon.config.ConfigSources; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.media.common.spi.MediaSupportProvider; /** @@ -100,6 +102,7 @@ public MessageBodyWriterContext writerContext() { /** * MediaSupport builder. */ + @Configured public static class Builder implements io.helidon.common.Builder, MediaContextBuilder { @@ -160,6 +163,11 @@ private Builder() { * @param config a {@link Config} * @return this {@link Builder} */ + @ConfiguredOption(key = "services", + type = MediaSupportProvider.class, + kind = ConfiguredOption.Kind.LIST, + required = true, + provider = true) public Builder config(Config config) { config.get("register-defaults").asBoolean().ifPresent(this::registerDefaults); config.get("discover-services").asBoolean().ifPresent(this::discoverServices); @@ -224,11 +232,12 @@ public Builder addStreamWriter(MessageBodyStreamWriter streamWriter) { } /** - * Whether defaults should be included. + * Whether default readers and writers should be registered. * * @param registerDefaults register defaults * @return this builder instance */ + @ConfiguredOption("true") public Builder registerDefaults(boolean registerDefaults) { this.registerDefaults = registerDefaults; return this; @@ -240,6 +249,7 @@ public Builder registerDefaults(boolean registerDefaults) { * @param discoverServices use Java Service Loader * @return this builder instance */ + @ConfiguredOption("false") public Builder discoverServices(boolean discoverServices) { this.discoverServices = discoverServices; return this; @@ -252,6 +262,7 @@ public Builder discoverServices(boolean discoverServices) { * @param filterServices filter services * @return this builder instance */ + @ConfiguredOption("false") public Builder filterServices(boolean filterServices) { this.filterServices = filterServices; return this; @@ -290,7 +301,7 @@ public void register(MessageBodyReaderContext readerContext, MessageBodyWriterCo .asList() .stream() .map(it -> it.create(Config.just(ConfigSources.create(servicesConfig.getOrDefault(it.configKey(), - new HashMap<>()))))) + new HashMap<>()))))) .collect(Collectors.toCollection(LinkedList::new)) .descendingIterator() .forEachRemaining(mediaService -> mediaService.register(readerContext, writerContext)); diff --git a/media/common/src/main/java/module-info.java b/media/common/src/main/java/module-info.java index 8f6a1cf7780..446b2783b55 100644 --- a/media/common/src/main/java/module-info.java +++ b/media/common/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -29,6 +29,8 @@ requires io.helidon.common.http; requires io.helidon.config; + requires static io.helidon.config.metadata; + exports io.helidon.media.common; exports io.helidon.media.common.spi; diff --git a/webclient/webclient/pom.xml b/webclient/webclient/pom.xml index c0267fa1cf1..576b07d9cd3 100644 --- a/webclient/webclient/pom.xml +++ b/webclient/webclient/pom.xml @@ -96,6 +96,18 @@ mockito-core test + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + diff --git a/webclient/webclient/src/main/java/io/helidon/webclient/Proxy.java b/webclient/webclient/src/main/java/io/helidon/webclient/Proxy.java index 89ce63ab713..71ddba141fc 100644 --- a/webclient/webclient/src/main/java/io/helidon/webclient/Proxy.java +++ b/webclient/webclient/src/main/java/io/helidon/webclient/Proxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -33,6 +33,8 @@ import io.helidon.common.configurable.LruCache; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.netty.channel.ChannelHandler; import io.netty.handler.proxy.HttpProxyHandler; @@ -408,6 +410,7 @@ public int hashCode() { /** * Fluent API builder for {@link Proxy}. */ + @Configured public static class Builder implements io.helidon.common.Builder { private final Set noProxyHosts = new HashSet<>(); @@ -498,6 +501,7 @@ public Builder config(Config config) { * @param type proxy type * @return updated builder instance */ + @ConfiguredOption("HTTP") public Builder type(ProxyType type) { this.type = type; return this; @@ -509,6 +513,7 @@ public Builder type(ProxyType type) { * @param host host * @return updated builder instance */ + @ConfiguredOption public Builder host(String host) { this.host = host; return this; @@ -520,6 +525,7 @@ public Builder host(String host) { * @param port port * @return updated builder instance */ + @ConfiguredOption public Builder port(int port) { this.port = port; return this; @@ -531,6 +537,7 @@ public Builder port(int port) { * @param username proxy username * @return updated builder instance */ + @ConfiguredOption public Builder username(String username) { this.username = username; return this; @@ -542,6 +549,7 @@ public Builder username(String username) { * @param password proxy password * @return updated builder instance */ + @ConfiguredOption(type = String.class) public Builder password(char[] password) { this.password = Arrays.copyOf(password, password.length); return this; @@ -563,6 +571,7 @@ public Builder password(char[] password) { * @param noProxyHost to exclude from proxying * @return updated builder instance */ + @ConfiguredOption(key = "no-proxy", kind = ConfiguredOption.Kind.LIST) public Builder addNoProxy(String noProxyHost) { noProxyHosts.add(noProxyHost); return this; @@ -574,6 +583,7 @@ public Builder addNoProxy(String noProxyHost) { * @param useIt use system selector * @return updated builder instance */ + @ConfiguredOption("false") public Builder useSystemSelector(boolean useIt) { if (useIt) { this.type = ProxyType.SYSTEM; diff --git a/webclient/webclient/src/main/java/io/helidon/webclient/WebClientConfiguration.java b/webclient/webclient/src/main/java/io/helidon/webclient/WebClientConfiguration.java index a46db13fbea..6452ba1220f 100644 --- a/webclient/webclient/src/main/java/io/helidon/webclient/WebClientConfiguration.java +++ b/webclient/webclient/src/main/java/io/helidon/webclient/WebClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. + * Copyright (c) 2019, 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. @@ -36,6 +36,8 @@ import io.helidon.common.context.Context; import io.helidon.config.Config; import io.helidon.config.DeprecatedConfig; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.media.common.MediaContext; import io.helidon.media.common.MediaContextBuilder; import io.helidon.media.common.MediaSupport; @@ -286,6 +288,7 @@ boolean relativeUris() { /** * A fluent API builder for {@link WebClientConfiguration}. */ + @Configured(root = true, prefix = "client", description = "Configuration of the HTTP client") static class Builder, T extends WebClientConfiguration> implements io.helidon.common.Builder, ParentingMediaContextBuilder, @@ -337,6 +340,7 @@ public T build() { * @param connectTimeout new connection timeout * @return updated builder instance */ + @ConfiguredOption(key = "connect-timeout-millis", type = Long.class, value = "60000") public B connectTimeout(Duration connectTimeout) { this.connectTimeout = connectTimeout; return me; @@ -348,6 +352,7 @@ public B connectTimeout(Duration connectTimeout) { * @param readTimeout new read timeout * @return updated builder instance */ + @ConfiguredOption(key = "read-timeout-millis", type = Long.class, value = "600000") public B readTimeout(Duration readTimeout) { this.readTimeout = readTimeout; return me; @@ -359,17 +364,19 @@ public B readTimeout(Duration readTimeout) { * @param followRedirects follow redirection * @return updated builder instance */ + @ConfiguredOption("false") public B followRedirects(boolean followRedirects) { this.followRedirects = followRedirects; return me; } /** - * Sets new user agent of the request. + * Name of the user agent which should be used. * * @param userAgent user agent * @return updated builder instance */ + @ConfiguredOption public B userAgent(String userAgent) { this.userAgent = LazyValue.create(() -> userAgent); return me; @@ -392,6 +399,7 @@ public B userAgent(LazyValue userAgent) { * @param proxy request proxy * @return updated builder instance */ + @ConfiguredOption public B proxy(Proxy proxy) { this.proxy = proxy; return me; @@ -403,6 +411,7 @@ public B proxy(Proxy proxy) { * @param webClientTls tls configuration * @return updated builder instance */ + @ConfiguredOption public B tls(WebClientTls webClientTls) { this.webClientTls = webClientTls; return me; @@ -414,6 +423,7 @@ public B tls(WebClientTls webClientTls) { * @param maxRedirects max redirects * @return updated builder instance */ + @ConfiguredOption("5") public B maxRedirects(int maxRedirects) { this.maxRedirects = maxRedirects; return me; @@ -449,6 +459,9 @@ public B cookieStore(CookieStore cookieStore) { * @param cookiePolicy cookie policy * @return updated builder instance */ + @ConfiguredOption(key = "cookies.automatic-store-enabled", + type = Boolean.class, + description = "Whether to allow automatic cookie storing") public B cookiePolicy(CookiePolicy cookiePolicy) { this.cookiePolicy = cookiePolicy; return me; @@ -461,6 +474,10 @@ public B cookiePolicy(CookiePolicy cookiePolicy) { * @param value cookie value * @return updated builder instance */ + @ConfiguredOption(key = "cookies.default-cookies", + type = Map.class, + description = "Default cookies to be used in each request. " + + "Each list entry has to have \"name\" and \"value\" node") public B defaultCookie(String key, String value) { defaultCookies.put(key, value); return me; @@ -473,6 +490,10 @@ public B defaultCookie(String key, String value) { * @param values header value * @return updated builder instance */ + @ConfiguredOption(key = "headers", + type = Map.class, + description = "Default headers to be used in each request. " + + "Each list entry has to have \"name\" and \"value\" node") public B defaultHeader(String key, List values) { clientHeaders.put(key, values); return me; @@ -485,12 +506,14 @@ public B defaultHeader(String key, List values) { * @param relativeUris relative URIs flag * @return updated builder instance */ + @ConfiguredOption("false") public B relativeUris(boolean relativeUris) { this.relativeUris = relativeUris; return me; } @Override + @ConfiguredOption(key = "media-support") public B mediaContext(MediaContext mediaContext) { writerContextParent(mediaContext.writerContext()); readerContextParent(mediaContext.readerContext()); @@ -512,6 +535,7 @@ public B context(Context context) { * * @return updated builder instance */ + @ConfiguredOption(type = String.class) public B uri(URI uri) { this.uri = uri; return me; @@ -591,6 +615,7 @@ B clientServices(List clientServices) { return me; } + @ConfiguredOption("true") B keepAlive(boolean keepAlive) { this.keepAlive = keepAlive; return me; diff --git a/webclient/webclient/src/main/java/io/helidon/webclient/WebClientTls.java b/webclient/webclient/src/main/java/io/helidon/webclient/WebClientTls.java index 9006a7eb93f..9b2ab62d8ec 100644 --- a/webclient/webclient/src/main/java/io/helidon/webclient/WebClientTls.java +++ b/webclient/webclient/src/main/java/io/helidon/webclient/WebClientTls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 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. @@ -27,6 +27,8 @@ import io.helidon.common.pki.KeyConfig; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; /** * Configuration of TLS requests. @@ -159,6 +161,7 @@ public int hashCode() { /** * Fluent API builder for {@link WebClientTls} instance. */ + @Configured public static final class Builder implements io.helidon.common.Builder { private boolean trustAll = false; @@ -178,6 +181,9 @@ private Builder() { * @param disableHostnameVerification disabled verification * @return updated builder instance */ + @ConfiguredOption(key = "server.disable-hostname-verification", + value = "false", + description = "Whether this client should perform hostname verification") public Builder disableHostnameVerification(boolean disableHostnameVerification) { this.disableHostnameVerification = disableHostnameVerification; return this; @@ -189,6 +195,9 @@ public Builder disableHostnameVerification(boolean disableHostnameVerification) * @param trustAll trust all certificates * @return updated builder instance */ + @ConfiguredOption(key = "server.trust-all", + value = "false", + description = "Whether this client should trust all certificates") public Builder trustAll(boolean trustAll) { this.trustAll = trustAll; return this; @@ -200,6 +209,9 @@ public Builder trustAll(boolean trustAll) { * @param keyStore trust store * @return updated builder instance */ + @ConfiguredOption(key = "server.truststore", + description = "Trust store which contains trusted certificates. " + + "If set, replaces those present by default") public Builder certificateTrustStore(KeyConfig keyStore) { Objects.requireNonNull(keyStore); certificates = keyStore.certs(); @@ -212,6 +224,8 @@ public Builder certificateTrustStore(KeyConfig keyStore) { * @param keyConfig key store * @return updated builder instance */ + @ConfiguredOption(key = "client.keystore", + description = "Client key store which contains client private key and certificate") public Builder clientKeyStore(KeyConfig keyConfig) { Objects.requireNonNull(keyConfig); keyConfig.privateKey().ifPresent(privateKey -> clientPrivateKey = privateKey); @@ -236,6 +250,10 @@ public Builder sslContext(SSLContext sslContext) { * @param allowedCipherSuite cipher suite * @return updated builder instance */ + @ConfiguredOption(key = "server.cipher-suite", + type = String.class, + kind = ConfiguredOption.Kind.LIST, + description = "List of allowed ciphers. If set, replaces those present by default") public Builder allowedCipherSuite(List allowedCipherSuite) { Objects.requireNonNull(allowedCipherSuite, "Allowed cipher suite cannot be null"); if (allowedCipherSuite.isEmpty()) { diff --git a/webclient/webclient/src/main/java/module-info.java b/webclient/webclient/src/main/java/module-info.java index 5d14054692a..6b4f86f2e50 100644 --- a/webclient/webclient/src/main/java/module-info.java +++ b/webclient/webclient/src/main/java/module-info.java @@ -38,6 +38,8 @@ requires io.netty.handler.proxy; requires io.netty.transport; + requires static io.helidon.config.metadata; + exports io.helidon.webclient; exports io.helidon.webclient.spi; From f148af8ba4b25d4b2127decd09932f77212e7e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Tue, 19 Jul 2022 12:01:12 +0200 Subject: [PATCH 41/51] Helidon Security Integration doc (#4565) Signed-off-by: David Kral --- docs/includes/attributes.adoc | 2 ++ docs/se/security/containers-integration.adoc | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 5567426d5a8..90b792dabc1 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -196,6 +196,8 @@ endif::[] :openapi-javadoc-base-url: {javadoc-base-url}/io.helidon.openapi :reactive-base-url: {javadoc-base-url}/io.helidon.common.reactive :scheduling-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.scheduling +:security-integration-jersey-base-url: {javadoc-base-url}/io.helidon.security.integration.jersey +:security-integration-webserver-base-url: {javadoc-base-url}/io.helidon.security.integration.webserver :webclient-javadoc-base-url: {javadoc-base-url}/io.helidon.webclient :webserver-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver :webserver-jersey-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.jersey diff --git a/docs/se/security/containers-integration.adoc b/docs/se/security/containers-integration.adoc index 9619b2c927d..3fbba6a7b43 100644 --- a/docs/se/security/containers-integration.adoc +++ b/docs/se/security/containers-integration.adoc @@ -23,6 +23,11 @@ include::{rootdir}/includes/se.adoc[] +== Contents + +- <> +- <> + == Cloud Security Container Integrations The following containers are integrated with Helidon Security: @@ -179,3 +184,6 @@ try { } ---- +== Reference +* link:{security-integration-jersey-base-url}/module-summary.html[Helidon Jersey Security Integration] +* link:{security-integration-webserver-base-url}/module-summary.html[Helidon WebServer Security Integration] \ No newline at end of file From 70d44efde64eb8596d20070240780e6c7eafc446 Mon Sep 17 00:00:00 2001 From: Andrii Serkes <74911628+aserkes@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:27:31 +0200 Subject: [PATCH 42/51] [New Doc PR] - Helidon SE Reactive Messaging #4303 (#4542) * Docs for Helidon SE Reactive Messaging #4303 Signed-off-by: aserkes --- docs/se/introduction.adoc | 2 +- docs/se/reactive-messaging.adoc | 699 ++++++++++++++++++++ docs/se/reactivemessaging/aq.adoc | 85 --- docs/se/reactivemessaging/connector.adoc | 206 ------ docs/se/reactivemessaging/introduction.adoc | 133 ---- docs/se/reactivemessaging/jms.adoc | 164 ----- docs/se/reactivemessaging/kafka.adoc | 173 ----- docs/sitegen.yaml | 10 +- 8 files changed, 702 insertions(+), 770 deletions(-) create mode 100644 docs/se/reactive-messaging.adoc delete mode 100644 docs/se/reactivemessaging/aq.adoc delete mode 100644 docs/se/reactivemessaging/connector.adoc delete mode 100644 docs/se/reactivemessaging/introduction.adoc delete mode 100644 docs/se/reactivemessaging/jms.adoc delete mode 100644 docs/se/reactivemessaging/kafka.adoc diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index b8974b4373b..0f7564904e1 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -105,7 +105,7 @@ Support OpenAPI from your application. //Reactive Messaging [CARD] .Reactive Messaging -[icon=message,link=reactivemessaging/introduction.adoc] +[icon=message,link=reactive-messaging.adoc] -- Use prepared tools for repetitive use case scenarios. -- diff --git a/docs/se/reactive-messaging.adoc b/docs/se/reactive-messaging.adoc new file mode 100644 index 00000000000..24945e31772 --- /dev/null +++ b/docs/se/reactive-messaging.adoc @@ -0,0 +1,699 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2020, 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. + +/////////////////////////////////////////////////////////////////////////////// + += Reactive Messaging +:h1Prefix: SE +:toc: +:toc-placement: preamble +:description: Reactive Messaging support in Helidon SE +:keywords: helidon, se, messaging +:feature-name: Reactive Messaging +:rootdir: {docdir}/.. + +== Contents + +- <> +- <> +- <> +** <> +** <> +** <> +** <> +*** <> +*** <> +*** <> +- <> +- <> + +== Overview + +Asynchronous messaging is a commonly used form of communication in the world of microservices. +While its possible to start building your reactive streams directly by combining operators and +connecting them to reactive APIs, with Helidon SE Reactive Messaging, you can now use prepared +tools for repetitive use case scenarios . + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.messaging + helidon-messaging + +---- + +== Usage + +For example connecting your streams to external services usually requires a lot of boiler-plate +code for configuration handling, backpressure propagation, acknowledgement and more. + +For such tasks there is a system of connectors, emitters and means to orchestrate them in Helidon, +called *Reactive Messaging*. It's basically an API for connecting and configuring +Connectors and Emitters with your reactive streams through so called <>. + +You may wonder how *Reactive Messaging* relates to +xref:../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging]. +As the making of connectors or even configuring them can be repetitive task leading to +the same results, Helidon SE Reactive Messaging supports very same configuration format +for connectors as its MicroProfile counterpart does. Also, MP Connectors are reusable in +Helidon SE Messaging with some limitation(there is no CDI in Helidon SE). +All <> in Helidon are made to be universally usable by Helidon MP and SE. + +=== Channel + +Channel is a named pair of `Publisher` and `Subscriber`. Channels can be connected together by +<>. Registering of `Publisher` or `Subscriber` for a channel can be done +by Messaging API, or configured implicitly for using registered <> +for generating such `Publisher` or `Subscriber`. + +[source,java] +.Example of simple channel: +---- +Channel channel1 = Channel.create("channel1"); + +Messaging.builder() + .publisher(channel1, Multi.just("message 1", "message 2") + .map(Message::of)) + .listener(channel1, s -> System.out.println("Intecepted message " + s)) + .build() + .start(); +---- + +=== Processor + +Processor is a typical reactive processor acting as a `Subscriber` to upstream and as a `Publisher` +to downstream. In terms of reactive messaging it is able to connect two <> to one +reactive stream. + +[source,java] +.Example of processor usage: +---- +Channel firstChannel = Channel.create("first-channel"); +Channel secondChannel = Channel.create("second-channel"); + +Messaging.builder() + .publisher(secondChannel, ReactiveStreams.of("test1", "test2", "test3") + .map(Message::of)) + .processor(secondChannel, firstChannel, ReactiveStreams.>builder() + .map(Message::getPayload) + .map(String::toUpperCase) + .map(Message::of) + ) + .subscriber(firstChannel, ReactiveStreams.>builder() + .peek(Message::ack) + .map(Message::getPayload) + .forEach(s -> System.out.println("Consuming message " + s))) + .build() + .start(); + +>Consuming message TEST1 +>Consuming message TEST2 +>Consuming message TEST3 +---- + +=== Message + +Reactive Messaging in Helidon SE uses the same concept of +message wrapping as MicroProfile messaging. +The only notable difference is that SE Messaging does almost no implicit or automatic +acknowledgement due to _no magic_ philosophy of Helidon SE. + +Only exception to this are variants of methods `Messaging.Builder#listener` and +`Messaging.Builder#processor` with consumer or function params, conveniently unwrapping payload +for you. After such implicit unwrapping is not possible to do a manual acknowledgement, therefore +implicit ack before callback is executed is necessary. + +=== Connectors + +Connector concept is a way for connecting <> to external sources. +To make <> +as easy and versatile as possible, Helidon SE Messaging uses same API for connectors +like xref:../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging] does. +This allows connectors to be usable in both flavors of Helidon with one limitation which is +that connector has to be able to work without CDI. + +Example of such a versatile connectors in Helidon: + +* <> +* <> +* <> + +==== Messaging Connector + +Connector for Reactive Messaging is a factory producing Publishers and Subscribers for +Channels in Reactive Messaging. Messaging connector is just an implementation of +`IncomingConnectorFactory`, `OutgoingConnectorFactory` or both. + +[source,java] +.Example connector `example-connector`: +---- +@Connector("example-connector") +public class ExampleConnector implements IncomingConnectorFactory, OutgoingConnectorFactory { + + @Override + public PublisherBuilder> getPublisherBuilder(Config config) { + return ReactiveStreams.of("foo", "bar") + .map(Message::of); + } + + @Override + public SubscriberBuilder, Void> getSubscriberBuilder(Config config) { + return ReactiveStreams.>builder() + .map(Message::getPayload) + .forEach(o -> System.out.println("Connector says: " + o)); + } +} +---- + +[source,yaml] +.Example of channel to connector mapping config: +---- +mp.messaging.outgoing.to-connector-channel.connector: example-connector +mp.messaging.incoming.from-connector-channel.connector: example-connector +---- + +[source,java] +.Example producing to connector: +---- +Config config = Config.create(); + +Messaging.builder() + .config(config) + .connector(new ExampleConnector()) + .publisher(Channel.create("to-connector-channel"), + ReactiveStreams.of("fee", "fie") + .map(Message::of) + ) + .build() + .start(); + +> Connector says: fee +> Connector says: fie +---- + +[source,java] +.Example consuming from connector: +---- +Messaging.builder() + .connector(new ExampleConnector()) + .subscriber(Channel.create("from-connector-channel"), + ReactiveStreams.>builder() + .peek(Message::ack) + .map(Message::getPayload) + .forEach(s -> System.out.println("Consuming: " + s)) + ) + .build() + .start(); + +> Consuming: foo +> Consuming: bar +---- + +===== Configuration for Messaging connector + +Messaging connector in Helidon SE can be configured explicitly by API or implicitly +by config following notation of link:https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_configuration[MicroProfile Reactive Messaging]. + +Configuration is being supplied to connector by Messaging implementation, +two mandatory attributes are always present: + +* `channel-name` name of the channel which has this connector configured as Publisher or Subscriber, `Channel.create('name-of-channel')` in case of explicit configuration or `mp.messaging.incoming.name-of-channel.connector: connector-name` in case of implicit config +* `connector` name of the connector `@Connector("connector-name")` + +[source,java] +.Example connector accessing configuration: +---- +@Connector("example-connector") +public class ExampleConnector implements IncomingConnectorFactory { + + @Override + public PublisherBuilder> getPublisherBuilder(final Config config) { + + String firstPropValue = config.getValue("first-test-prop", String.class);<1> + String secondPropValue = config.getValue("second-test-prop", String.class); + + return ReactiveStreams.of(firstPropValue, secondPropValue) + .map(Message::of); + } +} +---- +<1> Config context is merged from channel and connector contexts + +====== Explicit Config for Messaging connector + +An explicit config for channel's publisher is possible with `Channel.Builder#publisherConfig(Config config)` +and for subscriber with `Channel.Builder#subscriberConfig(Config config)`. +Supplied xref:config/introduction.adoc[Helidon Config] is merged with +mandatory attributes and any implicit config found. Resulting config is served to Connector. + +[source,java] +.Example consuming from Kafka connector with explicit config: +---- +String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); +String topic = config.get("app.kafka.topic").asString().get(); + +Channel fromKafka = Channel.builder()<1><2> + .name("from-kafka") + .publisherConfig(KafkaConnector.configBuilder() + .bootstrapServers(kafkaServer) + .groupId("example-group-" + session.getId()) + .topic(topic) + .autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST) + .enableAutoCommit(true) + .keyDeserializer(StringDeserializer.class) + .valueDeserializer(StringDeserializer.class) + .build() + ) + .build(); + +KafkaConnector kafkaConnector = KafkaConnector.create();<3> + +Messaging messaging = Messaging.builder() + .connector(kafkaConnector) + .listener(fromKafka, payload -> { + System.out.println("Kafka says: " + payload); + }) + .build() + .start(); +---- +<1> Prepare channel for connecting kafka connector with specific publisher configuration -> listener, +<2> Channel -> connector mapping is automatic when using `KafkaConnector.configBuilder()` +<3> Prepare Kafka connector, can be used by any channel + +====== Implicit Config for Messaging connector + +Implicit config without any hard-coding is possible with xref:config/introduction.adoc[Helidon Config] following notation of link:https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_configuration[MicroProfile Reactive Messaging]. + +[source,yaml] +.Example of channel to connector mapping config with custom properties: +---- +mp.messaging.incoming.from-connector-channel.connector: example-connector<1> +mp.messaging.incoming.from-connector-channel.first-test-prop: foo<2> +mp.messaging.connector.example-connector.second-test-prop: bar<3> +---- +<1> Channel -> Connector mapping +<2> Channel configuration properties +<3> Connector configuration properties + +[source,java] +.Example consuming from connector: +---- +Config config = Config.create(); + +Messaging.builder() + .config(config) + .connector(new ExampleConnector()) + .listener(Channel.create("from-connector-channel"), + s -> System.out.println("Consuming: " + s)) + .build() + .start(); + +> Consuming: foo +> Consuming: bar +---- + +==== Reusability in MP Messaging + +As the API is the same for xref:../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging] +connectors, all that is needed to make connector work in both ways is annotating it with +`@ApplicationScoped`. Such connector is treated as a bean in Helidon MP. + +For specific information about creating messaging connectors for Helidon MP visit +xref:../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging]. + +==== Kafka Connector + +[source,xml] +.Maven dependency +---- + + io.helidon.messaging.kafka + helidon-messaging-kafka + +---- + +===== Reactive Kafka Connector + +Connecting streams to Kafka with Reactive Messaging couldn't be easier. + +===== Explicit config with config builder for Kafka Connector + +[source,java] +.Example of consuming from Kafka: +---- +String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); +String topic = config.get("app.kafka.topic").asString().get(); + +Channel fromKafka = Channel.builder()<1><2> + .name("from-kafka") + .publisherConfig(KafkaConnector.configBuilder() + .bootstrapServers(kafkaServer) + .groupId("example-group-" + session.getId()) + .topic(topic) + .autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST) + .enableAutoCommit(true) + .keyDeserializer(StringDeserializer.class) + .valueDeserializer(StringDeserializer.class) + .build() + ) + .build(); + +KafkaConnector kafkaConnector = KafkaConnector.create();<3> + +Messaging messaging = Messaging.builder() + .connector(kafkaConnector) + .listener(fromKafka, payload -> { + System.out.println("Kafka says: " + payload); + }) + .build() + .start(); +---- +<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener +<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder() +<3> Prepare Kafka connector, can be used by any channel + +[source,java] +.Example of producing to Kafka: +---- +String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); +String topic = config.get("app.kafka.topic").asString().get(); + +Channel toKafka = Channel.builder()<1><2> + .subscriberConfig(KafkaConnector.configBuilder() + .bootstrapServers(kafkaServer) + .topic(topic) + .keySerializer(StringSerializer.class) + .valueSerializer(StringSerializer.class) + .build() + ).build(); + +KafkaConnector kafkaConnector = KafkaConnector.create();<3> + +messaging = Messaging.builder() + .publisher(toKafka, Multi.just("test1", "test2").map(Message::of)) + .connector(kafkaConnector) + .build() + .start(); +---- +<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener +<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder() +<3> Prepare Kafka connector, can be used by any channel + +===== Implicit Helidon Config for Kafka Connector + +[source,yaml] +.Example of connector config: +---- +mp.messaging: + + incoming.from-kafka: + connector: helidon-kafka + topic: messaging-test-topic-1 + auto.offset.reset: latest # <1> + enable.auto.commit: true + group.id: example-group-id + + outgoing.to-kafka: + connector: helidon-kafka + topic: messaging-test-topic-1 + + connector: + helidon-kafka: + bootstrap.servers: localhost:9092 # <2> + key.serializer: org.apache.kafka.common.serialization.StringSerializer + value.serializer: org.apache.kafka.common.serialization.StringSerializer + key.deserializer: org.apache.kafka.common.serialization.StringDeserializer + value.deserializer: org.apache.kafka.common.serialization.StringDeserializer +---- + +<1> Kafka client consumer's property auto.offset.reset configuration for `from-kafka` channel only +<2> Kafka client's property link:{kafka-client-base-url}#consumerconfigs_bootstrap.servers[bootstrap.servers] configuration for all channels using the connector + +[source,java] +.Example of consuming from Kafka: +---- +Config config = Config.create(); + +Channel fromKafka = Channel.create("from-kafka"); + +KafkaConnector kafkaConnector = KafkaConnector.create();<1> + +Messaging messaging = Messaging.builder() + .config(config) + .connector(kafkaConnector) + .listener(fromKafka, payload -> { + System.out.println("Kafka says: " + payload); + }) + .build() + .start(); +---- +<1> Prepare Kafka connector, can be used by any channel + +[source,java] +.Example of producing to Kafka: +---- +Config config = Config.create(); + +Channel toKafka = Channel.create("to-kafka"); + +KafkaConnector kafkaConnector = KafkaConnector.create();<1> + +messaging = Messaging.builder() + .config(config) + .publisher(toKafka, Multi.just("test1", "test2").map(Message::of)) + .connector(kafkaConnector) + .build() + .start(); +---- +<1> Prepare Kafka connector, can be used by any channel + +Don't forget to check out the examples with pre-configured Kafka docker image, for easy testing: + +* https://github.com/oracle/helidon/tree/master/examples/messaging + +==== JMS Connector + +[source,xml] +.Maven dependency +---- + + io.helidon.messaging.jms + helidon-messaging-jms + +---- + +===== Reactive JMS Connector + +Connecting streams to JMS with Reactive Messaging couldn't be easier. + +===== Explicit config with config builder for JMS Connector + +[source,java] +.Example of consuming from JMS: +---- +Channel fromJms = Channel.builder()<1><2> + .name("from-jms") + .publisherConfig(JmsConnector.configBuilder() + .jndiInitialFactory(ActiveMQInitialContextFactory.class) + .jndiProviderUrl("tcp://127.0.0.1:61616") + .type(JmsConfigBuilder.Type.QUEUE) + .destination("se-example-queue-1") + .build() + ) + .build(); + +JmsConnector jmsConnector = JmsConnector.create();<3> + +Messaging messaging = Messaging.builder() + .connector(jmsConnector) + .listener(fromJms, payload -> { + System.out.println("Jms says: " + payload); + }) + .build() + .start(); +---- +<1> Prepare a channel for connecting jms connector with specific publisher configuration -> listener +<2> Channel -> connector mapping is automatic when using JmsConnector.configBuilder() +<3> Prepare JMS connector, can be used by any channel + +[source,java] +.Example of producing to JMS: +---- +Channel toJms = Channel.builder()<1><2> + .subscriberConfig(JmsConnector.configBuilder() + .jndiInitialFactory(ActiveMQInitialContextFactory.class) + .jndiProviderUrl("tcp://127.0.0.1:61616") + .type(JmsConfigBuilder.Type.QUEUE) + .destination("se-example-queue-1") + .build() + ).build(); + +JmsConnector jmsConnector = JmsConnector.create();<3> + +messaging = Messaging.builder() + .publisher(toJms, Multi.just("test1", "test2").map(Message::of)) + .connector(jmsConnector) + .build() + .start(); +---- +<1> Prepare a channel for connecting jms connector with specific publisher configuration -> listener +<2> Channel -> connector mapping is automatic when using JmsConnector.configBuilder() +<3> Prepare JMS connector, can be used by any channel + +===== Implicit Helidon Config for JMS Connector + +[source,yaml] +.Example of connector config: +---- +mp.messaging: + + incoming.from-jms: + connector: helidon-jms + destination: se-example-queue-1 + session-group-id: session-group-1 + type: queue + + outgoing.to-jms: + connector: helidon-jms + destination: se-example-queue-1 + type: queue + + connector: + helidon-jms: + jndi: + jms-factory: ConnectionFactory + env-properties: + java.naming.factory.initial: org.apache.activemq.jndi.ActiveMQInitialContextFactory + java.naming.provider.url: tcp://127.0.0.1:61616 + +---- + +[source,java] +.Example of consuming from JMS: +---- +Config config = Config.create(); + +Channel fromJms = Channel.create("from-jms"); + +JmsConnector jmsConnector = JmsConnector.create();<1> + +Messaging messaging = Messaging.builder() + .config(config) + .connector(jmsConnector) + .listener(fromJms, payload -> { + System.out.println("Jms says: " + payload); + }) + .build() + .start(); +---- +<1> Prepare JMS connector, can be used by any channel + +[source,java] +.Example of producing to JMS: +---- +Config config = Config.create(); + +Channel toJms = Channel.create("to-jms"); + +JmsConnector jmsConnector = JmsConnector.create();<1> + +messaging = Messaging.builder() + .config(config) + .publisher(toJms, Multi.just("test1", "test2").map(Message::of)) + .connector(jmsConnector) + .build() + .start(); +---- +<1> Prepare JMS connector, can be used by any channel + +Don't forget to check out the examples with pre-configured ActiveMQ docker image, for easy testing: + +* https://github.com/oracle/helidon/tree/master/examples/messaging + +==== AQ Connector + +[source,xml] +.Maven dependency +---- + + io.helidon.messaging.aq + helidon-messaging-aq + +---- + +===== Reactive Oracle AQ Connector + +===== Sending and receiving + +[source,java] +.Example of producing to and consuming from Oracle AQ: +---- +PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();<1> +pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); +pds.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=192.168.0.123)(Port=1521))(CONNECT_DATA=(SID=XE)))"); +pds.setUser("frank"); +pds.setPassword("frank"); + +AqConnector seConn = AqConnector.builder()<2> + .dataSource("test-ds", pds) + .build(); + +Channel toAq = Channel.builder()<3> + .name("toAq") + .subscriberConfig(AqConnector.configBuilder() + .queue("example_queue_1") + .dataSource("test-ds") + .build()) + .build(); + +Channel fromAq = Channel.builder()<4> + .name("fromAq") + .publisherConfig(AqConnector.configBuilder() + .queue("example_queue_1") + .dataSource("test-ds") + .build()) + .build(); + +Messaging.builder()<5> + .connector(seConn) + .publisher(toAq, Multi.just("Hello", "world", "from", "Oracle", "DB!").map(Message::of))<6> + .listener(fromAq, s -> System.out.pritln("Message received: "+s))<7> + .build() + .start(); +---- +<1> Prepare Oracle UCP +<2> Setup AQ connector and provide datasource with an identifier `test-ds` +<3> Setup channel for sending messages to queue `example_queue_1` with datasource `test-ds` +<4> Setup channel for receiving messages from queue `example_queue_1` with datasource `test-ds` +<5> Register connector and channels +<6> Add a publisher for several test messages to publish them to `example_queue_1` immediately +<7> Subscribe callback for any message coming from `example_queue_1` + +== Configuration + +* <> +* <> +* <> +* <> +* <> + +== Reference + +* link:{microprofile-reactive-messaging-spec-url}[MicroProfile Reactive Messaging Specification] +* link:https://github.com/eclipse/microprofile-reactive-messaging[MicroProfile Reactive Messaging on GitHub] +* link:{helidon-github-tree-url}/examples/messaging[Helidon Messaging Examples] \ No newline at end of file diff --git a/docs/se/reactivemessaging/aq.adoc b/docs/se/reactivemessaging/aq.adoc deleted file mode 100644 index 856ac22b41f..00000000000 --- a/docs/se/reactivemessaging/aq.adoc +++ /dev/null @@ -1,85 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= AQ Connector -:toc: -:toc-placement: preamble -:description: Reactive Messaging support for Oracle AQ in Helidon SE -:keywords: helidon, se, messaging, jms, aq -:feature-name: AQ Connector -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.messaging.aq - helidon-messaging-aq - ----- - -== Reactive Oracle AQ Connector - -=== Sending and receiving - -[source,java] -.Example of producing to and consuming from Oracle AQ: ----- -PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();<1> -pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); -pds.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=192.168.0.123)(Port=1521))(CONNECT_DATA=(SID=XE)))"); -pds.setUser("frank"); -pds.setPassword("frank"); - -AqConnector seConn = AqConnector.builder()<2> - .dataSource("test-ds", pds) - .build(); - -Channel toAq = Channel.builder()<3> - .name("toAq") - .subscriberConfig(AqConnector.configBuilder() - .queue("example_queue_1") - .dataSource("test-ds") - .build()) - .build(); - -Channel fromAq = Channel.builder()<4> - .name("fromAq") - .publisherConfig(AqConnector.configBuilder() - .queue("example_queue_1") - .dataSource("test-ds") - .build()) - .build(); - -Messaging.builder()<5> - .connector(seConn) - .publisher(toAq, Multi.just("Hello", "world", "from", "Oracle", "DB!").map(Message::of))<6> - .listener(fromAq, s -> System.out.pritln("Message received: "+s))<7> - .build() - .start(); ----- -<1> Prepare Oracle UCP -<2> Setup AQ connector and provide datasource with an identifier `test-ds` -<3> Setup channel for sending messages to queue `example_queue_1` with datasource `test-ds` -<4> Setup channel for receiving messages from queue `example_queue_1` with datasource `test-ds` -<5> Register connector and channels -<6> Add a publisher for several test messages to publish them to `example_queue_1` immediately -<7> Subscribe callback for any message coming from `example_queue_1` \ No newline at end of file diff --git a/docs/se/reactivemessaging/connector.adoc b/docs/se/reactivemessaging/connector.adoc deleted file mode 100644 index 0532aa669d2..00000000000 --- a/docs/se/reactivemessaging/connector.adoc +++ /dev/null @@ -1,206 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Connector -:toc: -:toc-placement: preamble -:description: Reactive Messaging Connector in Helidon SE -:keywords: helidon, se, messaging, connector -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== Messaging Connector -Connector for Reactive Messaging is a factory producing Publishers and Subscribers for -Channels in Reactive Messaging. Messaging connector is just an implementation of -`IncomingConnectorFactory`, `OutgoingConnectorFactory` or both. - -[source,java] -.Example connector `example-connector`: ----- -@Connector("example-connector") -public class ExampleConnector implements IncomingConnectorFactory, OutgoingConnectorFactory { - - @Override - public PublisherBuilder> getPublisherBuilder(Config config) { - return ReactiveStreams.of("foo", "bar") - .map(Message::of); - } - - @Override - public SubscriberBuilder, Void> getSubscriberBuilder(Config config) { - return ReactiveStreams.>builder() - .map(Message::getPayload) - .forEach(o -> System.out.println("Connector says: " + o)); - } -} ----- - -[source,yaml] -.Example of channel to connector mapping config: ----- -mp.messaging.outgoing.to-connector-channel.connector: example-connector -mp.messaging.incoming.from-connector-channel.connector: example-connector ----- - -[source,java] -.Example producing to connector: ----- -Config config = Config.create(); - -Messaging.builder() - .config(config) - .connector(new ExampleConnector()) - .publisher(Channel.create("to-connector-channel"), - ReactiveStreams.of("fee", "fie") - .map(Message::of) - ) - .build() - .start(); - -> Connector says: fee -> Connector says: fie ----- - - -[source,java] -.Example consuming from connector: ----- -Messaging.builder() - .connector(new ExampleConnector()) - .subscriber(Channel.create("from-connector-channel"), - ReactiveStreams.>builder() - .peek(Message::ack) - .map(Message::getPayload) - .forEach(s -> System.out.println("Consuming: " + s)) - ) - .build() - .start(); - -> Consuming: foo -> Consuming: bar ----- - -=== Configuration -Messaging connector in Helidon SE can be configured explicitly by API or implicitly -by config following notation of link:{microprofile-reactive-messaging-spec-url}#_configuration[MicroProfile Reactive Messaging]. - -Configuration is being supplied to connector by Messaging implementation, -two mandatory attributes are always present: - -* `channel-name` name of the channel which has this connector configured as Publisher or Subscriber, `Channel.create('name-of-channel')` in case of explicit configuration or `mp.messaging.incoming.name-of-channel.connector: connector-name` in case of implicit config -* `connector` name of the connector `@Connector("connector-name")` - -[source,java] -.Example connector accessing configuration: ----- -@Connector("example-connector") -public class ExampleConnector implements IncomingConnectorFactory { - - @Override - public PublisherBuilder> getPublisherBuilder(final Config config) { - - String firstPropValue = config.getValue("first-test-prop", String.class);<1> - String secondPropValue = config.getValue("second-test-prop", String.class); - - return ReactiveStreams.of(firstPropValue, secondPropValue) - .map(Message::of); - } -} ----- -<1> Config context is merged from channel and connector contexts - -==== Explicit Config - -An explicit config for channel's publisher is possible with `Channel.Builder#publisherConfig(Config config)` -and for subscriber with `Channel.Builder#subscriberConfig(Config config)`. -Supplied xref:../config/introduction.adoc[Helidon Config] is merged with -mandatory attributes and any implicit config found. Resulting config is served to Connector. - -[source,java] -.Example consuming from Kafka connector with explicit config: ----- -String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); -String topic = config.get("app.kafka.topic").asString().get(); - -Channel fromKafka = Channel.builder()<1><2> - .name("from-kafka") - .publisherConfig(KafkaConnector.configBuilder() - .bootstrapServers(kafkaServer) - .groupId("example-group-" + session.getId()) - .topic(topic) - .autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST) - .enableAutoCommit(true) - .keyDeserializer(StringDeserializer.class) - .valueDeserializer(StringDeserializer.class) - .build() - ) - .build(); - -KafkaConnector kafkaConnector = KafkaConnector.create();<3> - -Messaging messaging = Messaging.builder() - .connector(kafkaConnector) - .listener(fromKafka, payload -> { - System.out.println("Kafka says: " + payload); - }) - .build() - .start(); ----- -<1> Prepare channel for connecting kafka connector with specific publisher configuration -> listener, -<2> Channel -> connector mapping is automatic when using `KafkaConnector.configBuilder()` -<3> Prepare Kafka connector, can be used by any channel - -==== Implicit Config -Implicit config without any hard-coding is possible with xref:../config/introduction.adoc[Helidon Config] following - notation of link:{microprofile-reactive-messaging-spec-url}#_configuration[MicroProfile Reactive Messaging]. - -[source,yaml] -.Example of channel to connector mapping config with custom properties: ----- -mp.messaging.incoming.from-connector-channel.connector: example-connector # <1> -mp.messaging.incoming.from-connector-channel.first-test-prop: foo # <2> -mp.messaging.connector.example-connector.second-test-prop: bar # <3> ----- -<1> Channel -> Connector mapping -<2> Channel configuration properties -<3> Connector configuration properties - -[source,java] -.Example consuming from connector: ----- -Config config = Config.create(); - -Messaging.builder() - .config(config) - .connector(new ExampleConnector()) - .listener(Channel.create("from-connector-channel"), - s -> System.out.println("Consuming: " + s)) - .build() - .start(); - -> Consuming: foo -> Consuming: bar ----- - -=== Re-usability in MP Messaging -As the API is the same for xref:../../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging] connectors, all that is needed to make connector work in both ways is annotating it with -`@ApplicationScoped`. Such connector is treated as a bean in Helidon MP. - -For specific information about creating messaging connectors for Helidon MP visit -xref:../../mp/reactivemessaging/introduction.adoc#Connector[Messaging Connector Bean]. diff --git a/docs/se/reactivemessaging/introduction.adoc b/docs/se/reactivemessaging/introduction.adoc deleted file mode 100644 index eda6319e83a..00000000000 --- a/docs/se/reactivemessaging/introduction.adoc +++ /dev/null @@ -1,133 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Reactive Messaging -:toc: -:toc-placement: preamble -:description: Reactive Messaging support in Helidon SE -:keywords: helidon, se, messaging -:feature-name: Reactive Messaging -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.messaging - helidon-messaging - ----- - -== Reactive Messaging - -Asynchronous messaging is a commonly used form of communication in the world of microservices. -While its possible to start building your reactive streams directly by combining operators and -connecting them to reactive APIs, with Helidon SE Reactive Messaging, you can now use prepared -tools for repetitive use case scenarios . - -For example connecting your streams to external services usually requires a lot of boiler-plate -code for configuration handling, backpressure propagation, acknowledgement and more. - -For such tasks there is a system of connectors, emitters and means to orchestrate them in Helidon, -called *Reactive Messaging*. It's basically an API for connecting and configuring -Connectors and Emitters with your reactive streams thru so called <>. - -You may wonder how *Reactive Messaging* relates to -xref:../../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging]. -As the making of connectors or even configuring them can be repetitive task leading to -the same results, Helidon SE Reactive Messaging supports very same configuration format -for connectors as its MicroProfile counterpart does. Also, MP Connectors are reusable in -Helidon SE Messaging with some limitation(there is no CDI in Helidon SE). -All Messaging connectors in Helidon are made to be universally usable by Helidon MP and SE. - -=== Channel -Channel is a named pair of `Publisher` and `Subscriber`. Channels can be connected together by -<>. Registering of `Publisher` or `Subscriber` for a channel can be done -by Messaging API, or configured implicitly for using registered xref:connector.adoc[connector] -for generating such `Publisher` or `Subscriber`. - -[source,java] -.Example of simple channel: ----- -Channel channel1 = Channel.create("channel1"); - -Messaging.builder() - .publisher(channel1, Multi.just("message 1", "message 2") - .map(Message::of)) - .listener(channel1, s -> System.out.println("Intecepted message " + s)) - .build() - .start(); ----- - -=== Processor -Processor is a typical reactive processor acting as a `Subscriber` to upstream and as a `Publisher` -to downstream. In terms of reactive messaging it is able to connect two <> to one -reactive stream. - -[source,java] -.Example of processor usage: ----- -Channel firstChannel = Channel.create("first-channel"); -Channel secondChannel = Channel.create("second-channel"); - -Messaging.builder() - .publisher(secondChannel, ReactiveStreams.of("test1", "test2", "test3") - .map(Message::of)) - .processor(secondChannel, firstChannel, ReactiveStreams.>builder() - .map(Message::getPayload) - .map(String::toUpperCase) - .map(Message::of) - ) - .subscriber(firstChannel, ReactiveStreams.>builder() - .peek(Message::ack) - .map(Message::getPayload) - .forEach(s -> System.out.println("Consuming message " + s))) - .build() - .start(); - ->Consuming message TEST1 ->Consuming message TEST2 ->Consuming message TEST3 ----- - -=== Message -Reactive Messaging in Helidon SE uses the same concept of -message wrapping as MicroProfile messaging. -The only notable difference is that SE Messaging does almost no implicit or automatic -acknowledgement due to _no magic_ philosophy of Helidon SE. - -Only exception to this are variants of methods `Messaging.Builder#listener` and -`Messaging.Builder#processor` with consumer or function params, conveniently unwrapping payload -for you. After such implicit unwrapping is not possible to do a manual acknowledgement, therefore -implicit ack before callback is executed is necessary. - -=== Connector -Connector concept is a way for connecting <> to external sources. -To make creation and usage of connectors -as easy and versatile as possible, Helidon SE Messaging uses same API for connectors -like xref:../../mp/reactivemessaging/introduction.adoc[MicroProfile Reactive Messaging] does. -This allows connectors to be usable in both flavors of Helidon with one limitation which is -that connector has to be able to work without CDI. - -Example of such a versatile connectors in Helidon: - - * xref:kafka.adoc[Kafka connector] - * xref:jms.adoc[JMS connector] diff --git a/docs/se/reactivemessaging/jms.adoc b/docs/se/reactivemessaging/jms.adoc deleted file mode 100644 index fda759570f7..00000000000 --- a/docs/se/reactivemessaging/jms.adoc +++ /dev/null @@ -1,164 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= JMS Connector -:toc: -:toc-placement: preamble -:description: Reactive Messaging support for JMS in Helidon SE -:keywords: helidon, se, messaging, jms -:feature-name: JMS Connector -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.messaging.jms - helidon-messaging-jms - ----- - -== Reactive JMS Connector -Connecting streams to JMS with Reactive Messaging couldn't be easier. - -=== Explicit config with config builder - -[source,java] -.Example of consuming from JMS: ----- -Channel fromJms = Channel.builder()<1><2> - .name("from-jms") - .publisherConfig(JmsConnector.configBuilder() - .jndiInitialFactory(ActiveMQInitialContextFactory.class) - .jndiProviderUrl("tcp://127.0.0.1:61616") - .type(JmsConfigBuilder.Type.QUEUE) - .destination("se-example-queue-1") - .build() - ) - .build(); - -JmsConnector jmsConnector = JmsConnector.create();<3> - -Messaging messaging = Messaging.builder() - .connector(jmsConnector) - .listener(fromJms, payload -> { - System.out.println("Jms says: " + payload); - }) - .build() - .start(); ----- -<1> Prepare a channel for connecting jms connector with specific publisher configuration -> listener -<2> Channel -> connector mapping is automatic when using JmsConnector.configBuilder() -<3> Prepare JMS connector, can be used by any channel - -[source,java] -.Example of producing to JMS: ----- -Channel toJms = Channel.builder()<1><2> - .subscriberConfig(JmsConnector.configBuilder() - .jndiInitialFactory(ActiveMQInitialContextFactory.class) - .jndiProviderUrl("tcp://127.0.0.1:61616") - .type(JmsConfigBuilder.Type.QUEUE) - .destination("se-example-queue-1") - .build() - ).build(); - -JmsConnector jmsConnector = JmsConnector.create();<3> - -messaging = Messaging.builder() - .publisher(toJms, Multi.just("test1", "test2").map(Message::of)) - .connector(jmsConnector) - .build() - .start(); ----- -<1> Prepare a channel for connecting jms connector with specific publisher configuration -> listener -<2> Channel -> connector mapping is automatic when using JmsConnector.configBuilder() -<3> Prepare JMS connector, can be used by any channel - -=== Implicit Helidon Config - -[source,yaml] -.Example of connector config: ----- -mp.messaging: - - incoming.from-jms: - connector: helidon-jms - destination: se-example-queue-1 - session-group-id: session-group-1 - type: queue - - outgoing.to-jms: - connector: helidon-jms - destination: se-example-queue-1 - type: queue - - connector: - helidon-jms: - jndi: - jms-factory: ConnectionFactory - env-properties: - java.naming.factory.initial: org.apache.activemq.jndi.ActiveMQInitialContextFactory - java.naming.provider.url: tcp://127.0.0.1:61616 - ----- - -[source,java] -.Example of consuming from JMS: ----- -Config config = Config.create(); - -Channel fromJms = Channel.create("from-jms"); - -JmsConnector jmsConnector = JmsConnector.create();<1> - -Messaging messaging = Messaging.builder() - .config(config) - .connector(jmsConnector) - .listener(fromJms, payload -> { - System.out.println("Jms says: " + payload); - }) - .build() - .start(); ----- -<1> Prepare JMS connector, can be used by any channel - -[source,java] -.Example of producing to JMS: ----- -Config config = Config.create(); - -Channel toJms = Channel.create("to-jms"); - -JmsConnector jmsConnector = JmsConnector.create();<1> - -messaging = Messaging.builder() - .config(config) - .publisher(toJms, Multi.just("test1", "test2").map(Message::of)) - .connector(jmsConnector) - .build() - .start(); ----- -<1> Prepare JMS connector, can be used by any channel - -Don't forget to check out the examples with pre-configured ActiveMQ docker image, for easy testing: - -* link:{helidon-github-tree-url}/examples/messaging diff --git a/docs/se/reactivemessaging/kafka.adoc b/docs/se/reactivemessaging/kafka.adoc deleted file mode 100644 index 2311647cac1..00000000000 --- a/docs/se/reactivemessaging/kafka.adoc +++ /dev/null @@ -1,173 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Kafka Connector -:toc: -:toc-placement: preamble -:description: Reactive Messaging support for Kafka in Helidon SE -:keywords: helidon, se, messaging, kafka -:feature-name: Kafka Connector -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.messaging.kafka - helidon-messaging-kafka - ----- - -== Reactive Kafka Connector - -Connecting streams to Kafka with Reactive Messaging couldn't be easier. - -=== Explicit config with config builder - -[source,java] -.Example of consuming from Kafka: ----- -String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); -String topic = config.get("app.kafka.topic").asString().get(); - -Channel fromKafka = Channel.builder()<1><2> - .name("from-kafka") - .publisherConfig(KafkaConnector.configBuilder() - .bootstrapServers(kafkaServer) - .groupId("example-group-" + session.getId()) - .topic(topic) - .autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST) - .enableAutoCommit(true) - .keyDeserializer(StringDeserializer.class) - .valueDeserializer(StringDeserializer.class) - .build() - ) - .build(); - -KafkaConnector kafkaConnector = KafkaConnector.create();<3> - -Messaging messaging = Messaging.builder() - .connector(kafkaConnector) - .listener(fromKafka, payload -> { - System.out.println("Kafka says: " + payload); - }) - .build() - .start(); ----- -<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener -<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder() -<3> Prepare Kafka connector, can be used by any channel - -[source,java] -.Example of producing to Kafka: ----- -String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get(); -String topic = config.get("app.kafka.topic").asString().get(); - -Channel toKafka = Channel.builder()<1><2> - .subscriberConfig(KafkaConnector.configBuilder() - .bootstrapServers(kafkaServer) - .topic(topic) - .keySerializer(StringSerializer.class) - .valueSerializer(StringSerializer.class) - .build() - ).build(); - -KafkaConnector kafkaConnector = KafkaConnector.create();<3> - -messaging = Messaging.builder() - .publisher(toKafka, Multi.just("test1", "test2").map(Message::of)) - .connector(kafkaConnector) - .build() - .start(); ----- -<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener -<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder() -<3> Prepare Kafka connector, can be used by any channel - -=== Implicit Helidon Config - -[source,yaml] -.Example of connector config: ----- -mp.messaging: - - incoming.from-kafka: - connector: helidon-kafka - topic: messaging-test-topic-1 - auto.offset.reset: latest - enable.auto.commit: true - group.id: example-group-id - - outgoing.to-kafka: - connector: helidon-kafka - topic: messaging-test-topic-1 - - connector: - helidon-kafka: - bootstrap.servers: localhost:9092 - key.serializer: org.apache.kafka.common.serialization.StringSerializer - value.serializer: org.apache.kafka.common.serialization.StringSerializer - key.deserializer: org.apache.kafka.common.serialization.StringDeserializer - value.deserializer: org.apache.kafka.common.serialization.StringDeserializer ----- - -[source,java] -.Example of consuming from Kafka: ----- -Config config = Config.create(); - -Channel fromKafka = Channel.create("from-kafka"); - -KafkaConnector kafkaConnector = KafkaConnector.create();<1> - -Messaging messaging = Messaging.builder() - .config(config) - .connector(kafkaConnector) - .listener(fromKafka, payload -> { - System.out.println("Kafka says: " + payload); - }) - .build() - .start(); ----- -<1> Prepare Kafka connector, can be used by any channel - -[source,java] -.Example of producing to Kafka: ----- -Config config = Config.create(); - -Channel toKafka = Channel.create("to-kafka"); - -KafkaConnector kafkaConnector = KafkaConnector.create();<1> - -messaging = Messaging.builder() - .config(config) - .publisher(toKafka, Multi.just("test1", "test2").map(Message::of)) - .connector(kafkaConnector) - .build() - .start(); ----- -<1> Prepare Kafka connector, can be used by any channel - -Don't forget to check out the examples with pre-configured Kafka docker image, for easy testing: - -* link:{helidon-github-tree-url}/examples/messaging \ No newline at end of file diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index bbfac482aec..feff72bf02c 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -197,18 +197,12 @@ backend: sources: - "engine.adoc" - "rsoperators.adoc" - - type: "MENU" + - type: "PAGE" title: "Reactive Messaging" - dir: "reactivemessaging" + source: "reactive-messaging.adoc" glyph: type: "icon" value: "message" - sources: - - "introduction.adoc" - - "connector.adoc" - - "kafka.adoc" - - "jms.adoc" - - "aq.adoc" - type: "PAGE" title: "Reactive Webserver" source: "webserver.adoc" From d3168cfb7a25e0dfeb100878328b2ca6d32fec29 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Tue, 19 Jul 2022 19:48:44 +0200 Subject: [PATCH 43/51] Enable Database MP tests (within build + generated code) (#4570) Signed-off-by: tvallin --- archetypes/helidon/src/main/archetype/common/sources.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/archetypes/helidon/src/main/archetype/common/sources.xml b/archetypes/helidon/src/main/archetype/common/sources.xml index 189fe1408e5..b2e89ea4091 100644 --- a/archetypes/helidon/src/main/archetype/common/sources.xml +++ b/archetypes/helidon/src/main/archetype/common/sources.xml @@ -27,18 +27,12 @@ src/*/java/**/*.java.mustache - - src/test/**/*.mustache - files src/*/resources/**/*.mustache - - src/test/resources/**/*.mustache - files @@ -47,7 +41,6 @@ **/*.mustache - src/test/resources/** From 6bbb0ccca4b91287de99967d060666f4b8829d04 Mon Sep 17 00:00:00 2001 From: Keith Lustria <34235093+klustria@users.noreply.github.com> Date: Tue, 19 Jul 2022 14:30:19 -0700 Subject: [PATCH 44/51] Update/Reformat Helidon MP and SE gRPC documents (#4569) * Update/Reformat Helidon MP and SE gRPC documents * Update @ConfiguredOption(s) and grpcserver paramter to grpc in the SE gRPC config document --- docs/config/config_reference.adoc | 3 + ...don_grpc_client_GrpcChannelDescriptor.adoc | 54 + ...o_helidon_grpc_core_GrpcTlsDescriptor.adoc | 53 + ...n_grpc_server_GrpcServerConfiguration.adoc | 59 ++ docs/mp/grpc/{mp-clients.adoc => client.adoc} | 260 ++--- ...ervices.adoc => server-side-services.adoc} | 106 +- docs/mp/introduction/introduction.adoc | 2 +- docs/se/grpc/client-configuration.adoc | 59 -- docs/se/grpc/client-introduction.adoc | 88 -- ...client-implementation.adoc => client.adoc} | 315 +++--- docs/se/grpc/configuration.adoc | 69 -- docs/se/grpc/health-checks.adoc | 119 --- docs/se/grpc/interceptors.adoc | 126 --- docs/se/grpc/introduction.adoc | 79 -- docs/se/grpc/marshalling.adoc | 2 +- docs/se/grpc/metrics.adoc | 196 ---- docs/se/grpc/routing.adoc | 104 -- docs/se/grpc/security.adoc | 233 ----- docs/se/grpc/server.adoc | 933 ++++++++++++++++++ docs/se/grpc/service-implementation.adoc | 165 ---- docs/se/introduction.adoc | 2 +- docs/sitegen.yaml | 22 +- grpc/client/pom.xml | 12 + .../grpc/client/GrpcChannelDescriptor.java | 9 +- grpc/client/src/main/java/module-info.java | 2 + grpc/core/pom.xml | 12 + .../helidon/grpc/core/GrpcTlsDescriptor.java | 9 +- grpc/core/src/main/java/module-info.java | 2 + grpc/server/pom.xml | 12 + .../grpc/server/GrpcServerConfiguration.java | 10 + grpc/server/src/main/java/module-info.java | 2 + 31 files changed, 1577 insertions(+), 1542 deletions(-) create mode 100644 docs/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc create mode 100644 docs/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc create mode 100644 docs/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc rename docs/mp/grpc/{mp-clients.adoc => client.adoc} (69%) rename docs/mp/grpc/{mp-server-side-services.adoc => server-side-services.adoc} (73%) delete mode 100644 docs/se/grpc/client-configuration.adoc delete mode 100644 docs/se/grpc/client-introduction.adoc rename docs/se/grpc/{client-implementation.adoc => client.adoc} (57%) delete mode 100644 docs/se/grpc/configuration.adoc delete mode 100644 docs/se/grpc/health-checks.adoc delete mode 100644 docs/se/grpc/interceptors.adoc delete mode 100644 docs/se/grpc/introduction.adoc delete mode 100644 docs/se/grpc/metrics.adoc delete mode 100644 docs/se/grpc/routing.adoc delete mode 100644 docs/se/grpc/security.adoc create mode 100644 docs/se/grpc/server.adoc delete mode 100644 docs/se/grpc/service-implementation.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index 7d3e7b08034..bf779462f0e 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -34,6 +34,9 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_faulttolerance_Retry_DelayingRetryPolicy.adoc[DelayingRetryPolicy (faulttolerance.Retry)] - xref:{rootdir}/config/io_helidon_security_providers_common_EvictableCache.adoc[EvictableCache (security.providers.common)] - xref:{rootdir}/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc[GoogleTokenProvider (security.providers.google.login)] +- xref:{rootdir}/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc[GrpcChannelDescriptor (grpc.client)] +- xref:{rootdir}/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc[GrpcServerConfiguration (grpc.server)] +- xref:{rootdir}/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc[GrpcTlsDescriptor (grpc.core)] - 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)] - xref:{rootdir}/config/io_helidon_health_HealthSupport.adoc[HealthSupport (health)] diff --git a/docs/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc b/docs/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc new file mode 100644 index 00000000000..63e4b534082 --- /dev/null +++ b/docs/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.grpc.client.GrpcChannelDescriptor +:keywords: helidon, config, io.helidon.grpc.client.GrpcChannelDescriptor +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.grpc.client.GrpcChannelDescriptor +include::{rootdir}/includes/attributes.adoc[] + += GrpcChannelDescriptor (grpc.client) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.grpc.client/io/helidon/grpc/client/GrpcChannelDescriptor.html[io.helidon.grpc.client.GrpcChannelDescriptor] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`host` |string |`localhost` |Set the host name to connect. +|`port` |int |`1408` |Set the port that will be used to connect to the server. +|`target` |string |{nbsp} |Set the target string, which can be either a valid io.grpc.NameResolver + compliant URI, or an authority string. +|`tls` |xref:{rootdir}/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc[GrpcTlsDescriptor] |{nbsp} |Set the GrpcTlsDescriptor. If `tlsDescriptor` is null or if the `tlsDescriptor.isEnabled()` is false, + then no TLS will be used. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc b/docs/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc new file mode 100644 index 00000000000..a652686862d --- /dev/null +++ b/docs/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.grpc.core.GrpcTlsDescriptor +:keywords: helidon, config, io.helidon.grpc.core.GrpcTlsDescriptor +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.grpc.core.GrpcTlsDescriptor +include::{rootdir}/includes/attributes.adoc[] + += GrpcTlsDescriptor (grpc.core) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.grpc.core/io/helidon/grpc/core/GrpcTlsDescriptor.html[io.helidon.grpc.core.GrpcTlsDescriptor] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`enabled` |boolean |`true` |Enable or disable TLS. If enabled is false then the rest of the TLS configuration properties are ignored. +|`jdk-ssl` |boolean |`false` |Sets the type of SSL implementation to be used. +|`tls-ca-cert` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Set the CA (certificate authority) certificate path. +|`tls-cert` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Set the client tlsCert path. Required only if mutual auth is desired. +|`tls-key` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Set the client private key path. Required only if mutual auth is desired. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc b/docs/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc new file mode 100644 index 00000000000..53ed5837be1 --- /dev/null +++ b/docs/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.grpc.server.GrpcServerConfiguration +:keywords: helidon, config, io.helidon.grpc.server.GrpcServerConfiguration +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.grpc.server.GrpcServerConfiguration +include::{rootdir}/includes/attributes.adoc[] + += GrpcServerConfiguration (grpc.server) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.grpc.server/io/helidon/grpc/server/GrpcServerConfiguration.html[io.helidon.grpc.server.GrpcServerConfiguration] + + + + +== Configuration options + + + +Optional configuration options: +[cols="3,3,2,5a"] + +|=== +|key |type |default value |description + +|`name` |string |`grpc.server` |Set the name of the gRPC server. + + Configuration key: `name` +|`native` |boolean |`false` |Specify if native transport should be used. +|`port` |int |`1408` |Sets server port. If port is `0` or less then any available ephemeral port will be used. + + Configuration key: `port` +|`workers` |int |`Number of processors available to the JVM` |Sets a count of threads in pool used to process HTTP requests. + Default value is `CPU_COUNT * 2`. + + Configuration key: `workers` + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/mp/grpc/mp-clients.adoc b/docs/mp/grpc/client.adoc similarity index 69% rename from docs/mp/grpc/mp-clients.adoc rename to docs/mp/grpc/client.adoc index ca6a3e5379c..c0f0b6a2a82 100644 --- a/docs/mp/grpc/mp-clients.adoc +++ b/docs/mp/grpc/client.adoc @@ -16,17 +16,26 @@ /////////////////////////////////////////////////////////////////////////////// -= gRPC MicroProfile Clients += gRPC MicroProfile Client :description: Building Helidon gRPC MicroProfile Clients -:keywords: helidon, grpc, microprofile, micro-profile +:keywords: helidon, java, grpc, microprofile, micro-profile, mp :feature-name: gRPC MicroProfile Clients :rootdir: {docdir}/../.. :microprofile-bundle: false include::{rootdir}/includes/mp.adoc[] +== Contents + +- <> +- <> +- <> +- <> +- <> + +== Overview Building Java gRPC clients using the Helidon MP gRPC APIs is very simple and removes a lot of the boiler plate code typically -associated to more traditional approaches to writing gRPC Java clients. At it simplest a gRPC Java client can be written using +associated to more traditional approaches of writing gRPC Java clients. At its simplest, a gRPC Java client can be written using nothing more than a suitably annotated interface. include::{rootdir}/includes/dependencies.adoc[] @@ -39,105 +48,32 @@ include::{rootdir}/includes/dependencies.adoc[] ---- +== API -== Building a gRPC Client -There are a few steps to building and using a gRPC client in Helidon MP. +The following annotations are used to work with Helidon MP gRPC Clients: -As discussed in the section on xref:mp-server-side-services.adoc[Server-Side Services] there are four different types of gRPC method. +* `@GrpcChannel` - an annotation used to inject a gRPC channel. +* `@InProcessGrpcChannel` - an annotation used to tell the Helidon MP gRPC API to inject an in-process channel. +* `@GrpcProxy` - an annotation used to mark an injection point for a gRPC service client proxy. +* `@Grpc` - an annotation used to mark a class as representing a gRPC service. -* Unary - a simple method with at most a single request value and returning at most a single response value. -* Server Streaming - a method that takes at most a single request value but may return zero or more response values. -* Client Streaming - a request that takes one or more request values and returns at most one response value. -* Bi-directional Streaming - a method that can take one or more request values and return zero or more response values. - -An as with the server-side APIS, the Helidon MP gRPC client APIs support a number of different method signatures for each of the -different gRPC method types. - -=== The Client Service Interface -The next step is to produce an interface with the service methods that the client requires. - -For example, suppose we have a simple server side service that has a unary method to convert a string to uppercase. -[source,java] -.Simple gRPC Service ----- -@ApplicationScoped -@io.helidon.microprofile.grpc.core.Grpc -public interface StringService { - - @io.helidon.microprofile.grpc.core.Unary - public String upper(String s) { - return s == null ? null : s.toUpperCase(); - } -} ----- - -The service has been written using the Helidon MP APIs but could just as easily be a traditional gRPC Java service generated from -Protobuf files. The client API is agnostic of the server side implementation, it only cares about the method type, the request -and response types and the type of Marshaller used to serialize the request and response. - -To write a client for the StringService all that is required is an interface. - -[source,java] -.Simple gRPC Service ----- -@ApplicationScoped -@io.helidon.microprofile.grpc.core.Grpc -public interface StringService { - - @io.helidon.microprofile.grpc.core.Unary - public String upper(String s); -} ----- - -There is no need to write any code to implement the client. The Helidon MP gRPC APIs will create a dynamic proxy for the interface -using the information from the annotations and method signatures. - -The interface in the example above used the same method signature as the server but this does not have to be the case, the -interface could have used any supported signature for a unary method, so for example it could just have easily been the standard -unary method signature: - -[source,java] -.Simple gRPC Service ----- -@ApplicationScoped -@io.helidon.microprofile.grpc.core.Grpc -public interface StringService { - - @io.helidon.microprofile.grpc.core.Unary - public void upper(String s, StreamObserver response); -} ----- - -We could also have made the client asynchronous by using one of the async method signatures: - -[source,java] -.Simple gRPC Service ----- -@ApplicationScoped -@io.helidon.microprofile.grpc.core.Grpc -public interface StringService { - - @io.helidon.microprofile.grpc.core.Unary - public CompletableFuture upper(String s); -} ----- - - -=== Configuring Channels -For a gRPC client to connect to a server it requires a Channel. The Helidon MP gRPC APIs provide a way to inject channels into +== Configuration +For a gRPC client to connect to a server, it requires a Channel. The Helidon MP gRPC APIs provide a way to inject channels into CDI beans that require them. +include::{rootdir}/config/io_helidon_grpc_client_GrpcChannelDescriptor.adoc[leveloffset=1, tag=config] + Channels are configured in the `grpc` section of the Helidon application configuration. The examples below use an `application.yaml` file but there are many other ways to use and override xref:../config/introduction.adoc[configuration in Helidon] +.General form of gRPC Channels configuration [source,yaml] -.application.yaml ---- grpc: - channels: <1> - test-server: <2> - host: localhost <3> - port: 1408 <4> + channels: # <1> + test-server: # <2> + host: localhost # <3> + port: 1408 # <4> ---- <1> Channels are configured in the`channels` section <2> Each sub-section is the Channel name that is then used to refer to this Channel in the application code @@ -146,8 +82,9 @@ grpc: While most client application only connect to a single server it is possible to configure multiple named channels if the client needs to connect to multiple servers. + +.Multiple gRPC Channels configuration example [source,yaml] -.application.yaml ---- grpc: channels: @@ -160,22 +97,24 @@ grpc: ---- The above example shows two channel configurations, one named `london` and the other `new-york`. -==== Configuring TLS +=== Configuring TLS It is also possible to configure a Channel to use TLS if the server is using TLS. +include::{rootdir}/config/io_helidon_grpc_core_GrpcTlsDescriptor.adoc[leveloffset=3] + +.TLS on gRPC Channels configuration example [source,yaml] -.application.yaml ---- grpc: channels: test-server: host: localhost port: 1408 - tls: <1> - enabled: true <2> - tls-cert-path: /certs/foo.cert <3> - tls-key-path: /certs/foo.key <4> - tls-ca-cert-path: /certs/ca.cert <5> + tls: # <1> + enabled: true # <2> + tls-cert-path: /certs/foo.cert # <3> + tls-key-path: /certs/foo.key # <4> + tls-ca-cert-path: /certs/ca.cert # <5> ---- <1> The `tls` section of the channel configuration is used to configure TLS. <2> The `enabled` value is used to enable or disable TLS for this channel. @@ -189,34 +128,36 @@ as a file. If `/certs/foo.cert` was a resource on the classpath the configuratio `tls-cert-resource-path` to load `/certs/foo.cert` from the classpath. The same applies to the `tls-key` and `tls-ca-cert` configuration keys. See the `io.helidon.common.configurable.Resource` class for details. +== Usage === Using Channels -Once one or more channels have been configured they can be used by client code. The simplest way to use a channel is to inject it -into beans using CDI. The Helidon gRPC client APIs have CDI producers that can provide `io.grpc.Channel` instances. +Once one or more channels have been configured, then they can be used by client code. The simplest way to use a channel is +to inject it into beans using CDI. The Helidon gRPC client APIs have CDI producers that can provide `io.grpc.Channel` instances. For example, a class might have an injectable `io.grpc.Channel` field: -[source,java] + .gRPC Channel Injection +[source,java] ---- - @Inject <1> - @GrpcChannel(name = "test-server") <2> + @Inject // <1> + @GrpcChannel(name = "test-server") // <2> private Channel channel; ---- <1> The `@Inject` annotation tells CDI to inject the channel. <2> The `@GrpcChannel` annotation is the qualifier that supplies the Channel name. This is the same name as used in the channel -configuration in the configuration examples above. +configuration in the examples provided in the <>. -When an instance of the CDI bean with the channel field is instantiated a channel will be injected into it. +When an instance of the CDI bean with the channel field is instantiated, a channel will be injected into it. ==== The In-Process Channel -If code is running in an application that is executing as part of a Helidon MP gRPC server there is a special in-process channel +If code is running in an application that is executing as part of a Helidon MP gRPC server, there is a special in-process channel available. This allows code executing on the server to make calls to gRPC services deployed on that server in the same way an -external client does. To inject an in-process channel a different qualifier annotation is used. +external client does. To inject an in-process channel, a different qualifier annotation is used. -[source,java] .gRPC in-Process Channel Injection +[source,java] ---- - @Inject <1> - @InProcessGrpcChannel <2> + @Inject // <1> + @InProcessGrpcChannel // <2> private Channel channel; ---- <1> The `@Inject` annotation is used the same as previously. @@ -224,10 +165,10 @@ external client does. To inject an in-process channel a different qualifier anno === Using the Client Interface in an Application -Now that there is a client interface and a Channel configuration we can use these in the client application. The simplest way is +Now that there is a client interface and a Channel configuration, we can then use these in the client application. The simplest way is to use the client in a CDI microprofile application. -In the application class that requires the client we can declare a field of the same type as the client service interface. +We can declare a field of the same type as the client service interface in the application class that requires the client. The field is then annotated so that CDI will inject the client proxy into the field. [source,java] @@ -236,10 +177,11 @@ The field is then annotated so that CDI will inject the client proxy into the fi @ApplicationScoped public class Client { - @Inject <1> - @GrpcProxy <2> - @GrpcChannel(name = "test-server") <3> + @Inject // <1> + @GrpcProxy // <2> + @GrpcChannel(name = "test-server") // <3> private StringService stringService; +} ---- <1> The `@Inject` annotation tells the CDI to inject the client implementation; the gRPC MP APIs have a bean provider that does this. @@ -247,8 +189,90 @@ public class Client { <3> The `@GrpcChannel` annotation identifies the gRPC channel to be used by the client. The name used in the annotation refers to a channel name in the application configuration. -Now when the CDI container instantiates instances of the `Client` it will inject a dynamic proxy into the `stringService` field +When the CDI container instantiates instances of the `Client`, it will inject a dynamic proxy into the `stringService` field and then any code in methods in the `Client` class can call methods on the `StringService` which will be translated to gRPC calls. -In the example above there is no need to directly use a `Channel` directly. The correct channel is added to the dynamic client +In the example above there is no need to use a `Channel` directly. The correct channel is added to the dynamic client proxy internally by the Helidon MP gRPC APIs. + +=== Building a gRPC Client +There are a few steps to building and using a gRPC client in Helidon MP. + +As discussed in the xref:server-side-services.adoc#_defining_service_methods[Defining Service methods] section of the xref:server-side-services.adoc[Server-Side Services] there are four different types of gRPC method. + +* Unary - a simple method with at most a single request value and returning at most a single response value. +* Server Streaming - a method that takes at most a single request value but may return zero or more response values. +* Client Streaming - a request that takes one or more request values and returns at most one response value. +* Bi-directional Streaming - a method that can take one or more request values and return zero or more response values. + +And as with the server-side APIs, the Helidon MP gRPC client APIs support a number of different method signatures for each of the +different gRPC method types. + +==== The Client Service Interface +The next step is to produce an interface with the service methods that the client requires. + +For example, suppose we have a simple server side service that has a unary method to convert a string to uppercase. +[source,java] +.Simple gRPC Service +---- +@ApplicationScoped +@io.helidon.microprofile.grpc.core.Grpc +public interface StringService { + + @io.helidon.microprofile.grpc.core.Unary + public String upper(String s) { + return s == null ? null : s.toUpperCase(); + } +} +---- + +The service has been written using the Helidon MP APIs but could just as easily be a traditional gRPC Java service generated from +Protobuf files. The client API is agnostic of the server side implementation, it only cares about the method type, the request +and response types and the type of Marshaller used to serialize the request and response. + +To write a client for the StringService all that is required is an interface. + +[source,java] +.Simple gRPC Service +---- +@ApplicationScoped +@io.helidon.microprofile.grpc.core.Grpc +public interface StringService { + + @io.helidon.microprofile.grpc.core.Unary + public String upper(String s); +} +---- + +There is no need to write any code to implement the client. The Helidon MP gRPC APIs will create a dynamic proxy for the interface +using the information from the annotations and method signatures. + +The interface in the example above used the same method signature as the server but this does not have to be the case. It +could have used any supported signature for a unary method. For example, it could just have easily been written using the standard +unary method signature: + +[source,java] +.Simple gRPC Service +---- +@ApplicationScoped +@io.helidon.microprofile.grpc.core.Grpc +public interface StringService { + + @io.helidon.microprofile.grpc.core.Unary + public void upper(String s, StreamObserver response); +} +---- + +We could also have made the client asynchronous by using one of the async method signatures: + +[source,java] +.Simple gRPC Service +---- +@ApplicationScoped +@io.helidon.microprofile.grpc.core.Grpc +public interface StringService { + + @io.helidon.microprofile.grpc.core.Unary + public CompletableFuture upper(String s); +} +---- diff --git a/docs/mp/grpc/mp-server-side-services.adoc b/docs/mp/grpc/server-side-services.adoc similarity index 73% rename from docs/mp/grpc/mp-server-side-services.adoc rename to docs/mp/grpc/server-side-services.adoc index ef0660643da..e24b05efb7d 100644 --- a/docs/mp/grpc/mp-server-side-services.adoc +++ b/docs/mp/grpc/server-side-services.adoc @@ -18,16 +18,24 @@ = gRPC MicroProfile Server Services :description: Helidon gRPC MicroProfile Server-Side Services -:keywords: helidon, grpc, microprofile, micro-profile +:keywords: helidon, java, grpc, microprofile, micro-profile, mp :feature-name: gRPC MicroProfile Server :rootdir: {docdir}/../.. :microprofile-bundle: false include::{rootdir}/includes/mp.adoc[] +== Contents + +- <> +- <> +- <> +- <> + +== Overview The gRPC Microprofile APIs are an extension to xref:../introduction/introduction.adoc[Helidon MP] to allow building of gRPC services and clients that integrate with the Microprofile APIs. Using Helidon gRPC MP makes building gRPC services -and clients an easier process that the traditional approach using Protobuf files and code generation. Services can be built +and clients an easier process compared to the traditional approach using Protobuf files and code generation. Services can be built using POJOs that are then discovered and deployed at runtime in the same way the Helidon MP discovers and deploys web resources in the MP http server. @@ -44,7 +52,22 @@ include::{rootdir}/includes/dependencies.adoc[] ---- -== Defining a Service + +== API +The following annotations are used to implement Helidon MP gRPC Services: + +* `@Grpc` - an annotation used to mark a class as representing a gRPC service. + +gRPC types of method: + +* <> - a simple method with at most a single request value and returning at most a single response value. +* <> - a method that takes at most a single request value but may return zero or more response values. +* <> - a request that takes one or more request values and returns at most one response value. +* <> - a method that can take one or more request values and return zero or more response values. + + +== Usage +=== Defining a Service The traditional approach to building Java gRPC services is to write Protobuf files describing the service and then use these to generate service stubs and finally implementing the service methods by extending the generated stub classes. @@ -66,24 +89,25 @@ public class StringService { ---- The code above is a simple service with a single unary method that just converts a String to uppercase. -The important parts in the example are the `@ApplicationScoped`, `@Grpc` and `@Unary` annotations; these, +The important parts in the example are the `@ApplicationScoped`, `@Grpc` and `@Unary` annotations. These, along with other annotations discussed later, allow the gRPC MP APIs to discover, configure and deploy the service. Of course Helidon gRPC MP does not preclude you from using the Protobuf files approach, traditional gRPC Java services also work in a gRPC MP server. -As already shown above a Helidon gRPC MP service is just an annotated POJO. To make a class a service it requires two +As already shown above, a Helidon gRPC MP service is just an annotated POJO. To make a class a service, it requires two annotations. [source,java] ---- -@ApplicationScoped <1> -@io.helidon.microprofile.grpc.core.Grpc <2> +@ApplicationScoped // <1> +@io.helidon.microprofile.grpc.core.Grpc // <2> public class StringService { +} ---- <1> The `ApplicationScoped` annotation is what makes the service implementation a CDI bean and hence discoverable. -<2> The `Grpc` annotation is what defines the class as a gRPC service so that when the bean is discovered it is +<2> The `Grpc` annotation is what defines the class as a gRPC service so that when the bean is discovered, it is then deployed by the gRPC MP server. === Service Name @@ -93,20 +117,20 @@ above the service name will be `StringService`. This can be change by supplying [source,java] ---- @ApplicationScoped -@io.helidon.microprofile.grpc.core.Grpc(name="Strings") <1> +@io.helidon.microprofile.grpc.core.Grpc(name="Strings") // <1> public class StringService { ---- -<1> in the example above the name of the deployed service will be `Strings`. +<1> in the example above, the name of the deployed service will be `Strings`. -== Defining Service Methods -Once a class is properly annotated to make it a gRPC MP service it needs to have service methods that implement the +=== Defining Service Methods +Once a class is properly annotated to make it a gRPC MP service, it needs to have service methods that implement the application business logic. In gRPC there are four different types of method: -* Unary - a simple method with at most a single request value and returning at most a single response value. -* Server Streaming - a method that takes at most a single request value but may return zero or more response values. -* Client Streaming - a request that takes one or more request values and returns at most one response value. -* Bi-directional Streaming - a method that can take one or more request values and return zero or more response values. +* `Unary` - a simple method with at most a single request value and returning at most a single response value. +* `Server Streaming` - a method that takes at most a single request value but may return zero or more response values. +* `Client Streaming` - a request that takes one or more request values and returns at most one response value. +* `Bi-directional Streaming` - a method that can take one or more request values and return zero or more response values. The Helidon gRPC MP API determines a method type by its annotation, which should be one of the following: [source,java] @@ -117,17 +141,17 @@ The Helidon gRPC MP API determines a method type by its annotation, which should @io.helidon.microprofile.grpc.core.Bidirectional ---- -=== Request an Response Types +==== Request and Response Types A gRPC service method typically takes a request parameter and returns a response value (streaming methods may take or return -multiple requests or responses). In traditional gRPC Java the types used for the request and response values must be +multiple requests or responses). In traditional gRPC Java, the types used for the request and response values must be Protobuf serializable classes but this is not the case with Helidon gRPC. Helidon supports xref:../../se/grpc/marshalling.adoc[pluggable Marshallers] and by default will support any Java primitive or Java `Serializable` as well as Protobuf types. Any type that can be marshalled by the built-in marshallers or custom supplied marshaller may be used as a request or response type. -=== Unary Methods +==== Unary Methods A unary gRPC method is the simplest type of service method. Typically a unary method takes a request value and returns a -response value but this does not have to be the case, a unary method could just as easily take no request parameter and/or +response value but this does not have to be the case. A unary method could just as easily take no request parameter and/or return no response. All of the signatures below are valid unary methods in Helidon gRPC MP. @@ -182,10 +206,10 @@ The various signatures supported above allow the service developer to choose the application business logic without needing to worry about handling standard gRPC Java requests and StreamObservers. The standard gRPC Java method signature is in the list above so it can still be used if required. -=== ServerStreaming Methods -A server streaming method receives a requests from the client and when the request stream is complete it sends back a stream +==== ServerStreaming Methods +A server streaming method receives a requests from the client and when the request stream is complete, it sends back a stream of response values. A traditional gRPC Java server streaming method takes two parameters, the request and a `StreamObserver` -that is used to send back the single response in the same way that a unary method sends a response. As with unary methods +that is used to send back the single response in the same way that a unary method sends a response. As with unary methods, Helidon gRPC MP supports different method signatures for server streaming methods. All of the signatures below are valid server streaming methods in Helidon gRPC MP. @@ -212,8 +236,8 @@ public Stream invoke(RequestType req) As with unary methods, the Helidon gRPC MP API supports multiple different method signatures for implementing server streaming methods. -=== ClientStreaming Methods -A client streaming method receives a stream of requests from the client and when the request stream is complete it sends back a +==== ClientStreaming Methods +A client streaming method receives a stream of requests from the client and when the request stream is complete, it sends back a response. A traditional gRPC Java client streaming method takes two `StreamObserver` parameters, one is the stream of client requests and the other is used to send back the single response in the same way that a unary method sends a response. As with unary methods Helidon gRPC MP supports different method signatures for client streaming methods. @@ -232,9 +256,9 @@ public StreamObserver invoke(CompletableFuture observ ---- -=== Bi-Directional Streaming Methods +==== Bi-Directional Streaming Methods A bidirectional streaming method is a method that is a constant stream of client requests and server responses. Other than -the standard gRPC Java `StreamObserver` there are not any other built in types that make sense to use to implement +the standard gRPC Java `StreamObserver`, there are not any other built in types that make sense to use to implement different method signatures for a bidirectional method so the only supported signature is the standard gRPC Java method. [source,java] @@ -243,15 +267,15 @@ different method signatures for a bidirectional method so the only supported sig public StreamObserver invoke(StreamObserver observer) ---- -== Deploying Protobuf Services -Whilst the examples above show how simple it is to write gRPC services with basic POJOs there may be cases where there is a +=== Deploying Protobuf Services +Whilst the examples above show how simple it is to write gRPC services with basic POJOs, there may be cases where there is a requirement to deploy services built the traditional way using gRPC Java generated classes or built as -xref:../../se/grpc/service-implementation.adoc[non-microprofile Helidon gRCP services]. +xref:../../se/grpc/server.adoc#_service_implementation[non-microprofile Helidon gRCP services]. -=== Annotate the Service Implementation -When the gRPC MP server is starting it will discover all CDI beans of type `io.grpc.BindableService`. Service sub-classes +==== Annotate the Service Implementation +When the gRPC MP server is starting, it will discover all CDI beans of type `io.grpc.BindableService`. Service sub-classes implemented the traditional way with code generation are instances of `BindableService` so by annotating the implementation class -with the `@ApplicationScoped` annotation they become discoverable and will be deployed into the gRPC server. +with the `@ApplicationScoped` annotation, they become discoverable and will be deployed into the gRPC server. [source,java] ---- @@ -260,8 +284,7 @@ public class StringService extends StringServiceGrpc.StringServiceImplBase { ---- -In exactly the same way, if a class is an implementation of `io.helidon.grpc.server.GrpcService` then by annotating the class with -the `@ApplicationScoped` annotation it will be discovered and deployed when the MP gRPC server starts. +In exactly the same way, if a class is an implementation of `io.helidon.grpc.server.GrpcService`, then it will be discovered and deployed when the MP gRPC server starts by simply annotating the class with the `@ApplicationScoped` annotation. [source,java] ---- @@ -269,14 +292,13 @@ the `@ApplicationScoped` annotation it will be discovered and deployed when the public class StringService implements GrpcService { ---- -=== Implement a GrpcMpExtension -If it is not possible to annotate the service class (for example the code is built by a third party) another way to deploy none +==== Implement a GrpcMpExtension +If it is not possible to annotate the service class (for example the code is built by a third party), another way to deploy none CDI bean services is to implement a gRPC MP server extension. The extension will then be called when the MP server is starting and be given the chance to add additional services for deployment. An extension should implement the `io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` interface. -For example, assuming that there was a gRPC service class called `StringService` that needed to be deployed an extension class -might look like this: +For example, assuming that there was a gRPC service class called `StringService` that needed to be deployed, an extension class might look like this: [source,java] ---- public class MyExtension @@ -290,9 +312,9 @@ public class MyExtension ---- <1> The `configure` method of the extension will be called to allow the extension to add extra configuration to the server. -<2> In this example an instance of the `StringService` is registered with the routing (as described in -the xref:../../se/grpc/routing.adoc[basic gRPC server documentation]). +<2> In this example, an instance of the `StringService` is registered with the routing (as described in +the xref:../../se/grpc/server.adoc#_grpc_server_routing[basic gRPC server documentation about Routing]). -The `GrpcMpExtension` instances are discovered and loaded using the service loader so for the example above to work a file +The `GrpcMpExtension` instances are discovered and loaded using the service loader so for the example above to work, a file `META-INF/services/io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` would need to be created that contained the names of the service implementations. diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index d1c818c0c6a..90f3ff4f907 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -98,7 +98,7 @@ Expose GraphQL API using Microprofile GraphQL. //gRPC [CARD] .gRPC -[icon=swap_horiz,link=../grpc/mp-server-side-services.adoc] +[icon=swap_horiz,link=../grpc/server-side-services.adoc] -- Build gRPC servers and clients. -- diff --git a/docs/se/grpc/client-configuration.adoc b/docs/se/grpc/client-configuration.adoc deleted file mode 100644 index 6118f3fca65..00000000000 --- a/docs/se/grpc/client-configuration.adoc +++ /dev/null @@ -1,59 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Client Configuration -:description: Helidon gRPC Client Configuration -:keywords: helidon, grpc, java, configuration -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Configure the gRPC client using the Helidon configuration framework, either programmatically or via a configuration file. - -As mentioned earlier, creating a `GrpcServiceClient` involves: - -1. Creating a `ClientServiceDescriptor` which describes the methods in the service that this client can invoke. -2. Creating a gRPC `Channel` through which the client communicates with the server. - -== Configuring the ClientServiceDescriptor - -=== Configuring the ClientServiceDescriptor in your code - -The only way to configure the `ClientServiceDescriptor` is in your application code. - -[source,java] ----- -ClientServiceDescriptor descriptor = ClientServiceDescriptor + - .builder(HelloService.class) // (1) - .unary("SayHello") // (2) - .build(); // (3) ----- - -1. Create a builder for a `ClientServiceDescriptor` for the `HelloService`. -2. Specify that the `HelloService` has a unary method named `SayHello`. There are many other methods in this class that allow you -to define `ClientStreaming`, `ServerStreaming` and `Bidirectional` methods. -3. Build the `ClientServiceDescriptor`. - -== Configuring the gRPC Channel - -gRPC allows various channel configurations (deadlines, retries, interceptors etc.) - -Please refer to gRPC documentation: https://grpc.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html. - - - diff --git a/docs/se/grpc/client-introduction.adoc b/docs/se/grpc/client-introduction.adoc deleted file mode 100644 index 2914db3ce11..00000000000 --- a/docs/se/grpc/client-introduction.adoc +++ /dev/null @@ -1,88 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Client Introduction -:description: Helidon gRPC Client Introduction -:keywords: helidon, grpc, java -:feature-name: gRPC Client -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Helidon gRPC Client provides a framework for creating link:http://grpc.io/[gRPC] client applications. The client framework -allows a uniform way to access gRPC services that use either Protobuf or some custom serialization format. It also allows access -to gRPC services that use either Java serialization, Protobuf or a custom serialization format. - -The class `GrpcServiceClient` acts as the client object for accessing a gRPC service. Creating a `GrpcServiceClient` involves: - -1. Creating a `ClientServiceDescriptor` which describes the methods in the service that this client can invoke. -2. Creating a gRPC `Channel` through which the client communicates with the server. - -In later sections in this document, you will see how to customize both `ClientServiceDescriptor` and the `Channel`. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.grpc - helidon-grpc-client - ----- - -== Quick Start - -First, create and run a minimalist `HelloService` gRPC server application as described in the -xref:introduction.adoc[gRPC Server] documentation. - -Assuming that the server is running on port 1408, create a client as follows: - -[source,java] ----- -public static void main(String[] args) throws Exception { - ClientServiceDescriptor descriptor = ClientServiceDescriptor.builder(HelloService.class) // (1) - .unary("SayHello") // (2) - .build(); - - Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // (3) - .usePlaintext() - .build(); - - GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor); // (4) - - CompletionStage future = client.unary("SayHello", "Helidon gRPC!!"); // (5) - System.out.println(future.get()); // (6) - -} ----- - -1. Create a `ClientServiceDescriptor` for the `HelloService`. -2. Add the `SayHello` unary method to the `ClientServiceDescriptor`. This method, by default, uses Java serialization for -marshalling and unmarshalling the request and response values. -3. Create a gRPC `Channel` that is communicates with the server that is running in localhost and on port 1408 (using plaintext). -4. Create the `GrpcServiceClient` that uses the above `Channel` and `ClientServiceDescriptor`. `GrpcClientService` represents -a client that can be used to define the set of methods described by the specified `ClientServiceDescriptor`. In our case, the -`ClientServiceDescriptor` defines one unary method called `SayHello`. -5. Invoke the `SayHello` method which returns a `CompletionStage`. -6. Print the result. - -The example above creates a very simple client to the gRPC server that by default uses Java serialization to marshall -requests and responses. - -We will look into deployment of "standard" gRPC services that use Protobuf for request and response marshalling, as well as -how you can configure custom marshallers, later in this document. diff --git a/docs/se/grpc/client-implementation.adoc b/docs/se/grpc/client.adoc similarity index 57% rename from docs/se/grpc/client-implementation.adoc rename to docs/se/grpc/client.adoc index 7fa9dcdca12..dd1c4ed97e7 100644 --- a/docs/se/grpc/client-implementation.adoc +++ b/docs/se/grpc/client.adoc @@ -16,48 +16,70 @@ /////////////////////////////////////////////////////////////////////////////// -= gRPC Client Implementation -:description: Helidon gRPC Client Implementation -:keywords: helidon, grpc, java += gRPC Client +:description: Helidon gRPC Client +:keywords: helidon, grpc, java, se +:feature-name: gRPC Client :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -Helidon gRPC client framework allows you to write gRPC clients to access any gRPC -service implementation. The benefits of using Helidon gRPC Client Framework include: +== Contents -* It provides a number of helper methods that make client implementation - significantly simpler. +- <> +- <> +- <> +- <> +- <> -* It allows you to configure some of the Helidon value-added features, such - as xref:security.adoc[security] and xref:metrics.adoc[metrics collection and interceptors] - down to the method level. +== Overview + +Helidon gRPC Client provides a framework for creating link:http://grpc.io/[gRPC] client applications. The client framework +allows a uniform way to access gRPC services that use either Protobuf or some custom serialization format. The benefits of using Helidon gRPC Client Framework include: +* It provides a number of helper methods that make client implementation +significantly simpler. +* It allows you to configure some of the Helidon value-added features, such +as xref:server.adoc#_security[security], xref:server.adoc#_service_metrics[metrics collection] and xref:server.adoc#_interceptors[interceptors] down to the method level. * It allows you to easily specify custom marshaller for requests and - responses if `protobuf` does not satisfy your needs. +responses if `protobuf` does not satisfy your needs. -== Client Implementation Basics +The class `GrpcServiceClient` acts as the client object for accessing a gRPC service. Creating a `GrpcServiceClient` involves: -* The first step to create a Helidon gRPC client application is to describe the set of methods in the gRPC service. Helidon -gRPC Client Framework (simply called the "Client framework" in the remainder of the document) provides a class called -`ClientServiceDescriptor` to describe the set of methods of a service that the client may invoke. +1. Creating a `ClientServiceDescriptor` which describes the methods in the service that this client can invoke. +2. Creating a gRPC `Channel` through which the client communicates with the server. + +In later sections in this document, you will see how to customize both `ClientServiceDescriptor` and the `Channel`. -There are three ways to build and initialize a `ClientServiceDescriptor`. -** The first option is to initialize `ClientServiceDescriptor` using `protoc` generated artifacts like +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.grpc + helidon-grpc-client + +---- + +== Usage +=== Client Implementation Basics + +. The first step to create a Helidon gRPC client application is to describe the set of methods in the gRPC service. Helidon +gRPC Client Framework (simply called the "Client framework" in the remainder of the document) provides a class called +`ClientServiceDescriptor` to describe the set of methods of a service that the client may invoke. There are three ways to build and initialize a `ClientServiceDescriptor`. +* The first option is to initialize `ClientServiceDescriptor` using `protoc` generated artifacts like `BindableService` or `io.grpc.ServiceDescriptor`. This option is possible if the gRPC service was built using `.proto` file. In this case the set of gRPC methods, their types and the appropriate marshallers are detected automatically. This is certainly the easiest way to initialize a `ClientServiceDescriptor`. -** The second option is to programmatically build the `ClientServiceDescriptor`. This option should be +* The next option is to programmatically build the `ClientServiceDescriptor`. This option should be taken if the service was *not* built from protobuf files or if the `protoc` generated artifacts are not available to the client. -** The third option is to load the method descriptions from a configuration file. (Not yet implemented). +* The last option is to load the method descriptions from a configuration file. (** Not yet implemented**). +. The next step is to create a gRPC `Channel` to use to communicate with the server. +. Finally, you create an instance of `GrpcServiceClient` passing the `ClientMethodDescriptor` and the `Channel` instances. -* The next step is to create a gRPC `Channel` to use to communicate with the server. - -* Finally, you create an instance of `GrpcServiceClient` passing the `ClientMethodDescriptor` and the `Channel` instances. - -== Creating gRPC clients from `protoc` generated artifacts +=== Creating gRPC clients from `protoc` generated artifacts As mentioned above, the easiest way to create a `ClientServiceDescriptor` is to create it from an `io.grpc.ServiceDescriptor` or from a `io.grpc.BindableService`. It is fairly trivial to obtain these from a service generated from artifacts generated @@ -87,7 +109,7 @@ If you run it through `protoc` it will generate a class (among other things) cal Assuming that the `StringService` server is running on port 1408, here is how you can create a Helidon gRPC Client that uses the Client Framework to invoke various types of gRPC methods. -=== Creating and initializing a ClientServiceDescriptor for StringService (generated from `protoc`) +==== Creating and initializing a ClientServiceDescriptor for StringService (generated from `protoc`) Lets build a class called `ProtoBasedStringServiceClient` that invokes the various types of gRPC methods that our `StringService` offers. @@ -101,22 +123,20 @@ public class ProtoBasedStringServiceClient { public ProtoBasedStringServiceClient() { ClientServiceDescriptor desc = ClientServiceDescriptor - .builder(StringService.getServiceDescriptor()) // (1) + .builder(StringService.getServiceDescriptor()) // <1> .build(); - Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // (2) + Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // <2> .usePlaintext().build(); - this.client = GrpcServiceClient.create(channel, desc); // (3) + this.client = GrpcServiceClient.create(channel, desc); // <3> } /** * Many gRPC methods take a {@link io.grpc.StreamObserver} as an argument. Lets * build a helper class that can be used in our example. */ - public static class StringMessageStream // (4) - implements StreamObserver { - + public static class StringMessageStream implements StreamObserver { // <4> @Override public void onNext(T value) { System.out.println("Received : " + value); @@ -135,22 +155,19 @@ public class ProtoBasedStringServiceClient { } ---- -1. Initialize the builder by specifying the `StringService`'s proto `ServiceDescriptor`. From -the `ServiceDescriptor` the builder detects the service name, the set of method names, and for -each method its type (like Unary, ServerStreaming etc.), the request and response types (and -hence their corresponding Marshallers) etc. - -2. We create a `Channel` to the service that is running on `localhost:1408`. - -3. Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor` +<1> Initialize the builder by specifying the `StringService's` proto `ServiceDescriptor`. From +the `ServiceDescriptor` the builder detects the service name, the set of method names, the type of +each method (like Unary, ServerStreaming, etc.), the request and response types (and +hence their corresponding Marshallers), etc. +<2> We create a `Channel` to the service that is running on `localhost:1408`. +<3> Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor` and `Channel`. This `client` reference will be used to invoke various gRPC methods in our `StringService` - -4. We define a static inner class that implements the `io.grpc.StreamObserver` interface. An instance +<4> We define a static inner class that implements the `io.grpc.StreamObserver` interface. An instance of this class can be used whereever a `io.grpc.StreamObserver` is required (like server streaming, bi-directional streaming methods). -=== Invoking a unary method on the StringService +==== Invoking a unary method on the StringService The Client Framework provides many helper methods to invoke gRPC unary methods. @@ -165,29 +182,27 @@ public class ProtoBasedStringServiceClient { public void invokeUnaryMethod() throws Exception { StringMessage input = StringMessage.newBuilder().setText("ABC").build(); - CompletableFuture result = client.unary("Lower", input); // (1) + CompletableFuture result = client.unary("Lower", input); // <1> - String lcase = client.blockingUnary("Lower", input); // (2) + String lcase = client.blockingUnary("Lower", input); // <2> StringMessageStream stream = new StringMessageStream(); - client.blockingUnary("Lower", input); // (3) + client.blockingUnary("Lower", input); // <3> } public static class StringMessageStream { /* code omitted */ } } ---- -1. This variant of the `unary` API takes the method name and a request object and returns +<1> This variant of the `unary` API takes the method name and a request object and returns a `CompletableFuture` where `` is the response type. Here we invoke the `Lower` method passing the input `StringMessage`. This method returns a `CompletableFuture` as response thus allowing the client to obtain the result asynchronously. - -2. This is simply a wrapper around the above method. This method blocks till the result is available. - -3. Here we create invoke the `unary` method by passing the `StringMessageStream` whose `onNext` method +<2> This is simply a wrapper around the above method. This method blocks till the result is available. +<3> Here, we create invoke the `unary` method by passing the `StringMessageStream` whose `onNext` method will be called (once) when the result is available. -=== Invoking a client streaming method on the StringService +==== Invoking a client streaming method on the StringService Lets invoke the `Join` method which causes the server to return a single result *after* the client has streamed the request values to the server. gRPC API expects the client application to provide @@ -211,24 +226,24 @@ public class ProtoBasedStringServiceClient { public void invokeClientStreamingWithIterable() throws Exception { String sentence = "A simple invocation of a client streaming method"; - Collection input = Arrays.stream(sentence.split(" ")) // (1) + Collection input = Arrays.stream(sentence.split(" ")) // <1> .map(w -> StringMessage.newBuilder().setText(w).build()) .collect(Collectors.toList()); CompletableFuture result = - grpcClient.clientStreaming("Join", input); // (2) + grpcClient.clientStreaming("Join", input); // <2> } public void invokeClientStreaming() throws Exception { String sentence = "A simple invocation of a client streaming method"; StringMessageStream responseStream = new StringMessageStream(); StreamObserver clientStream = - grpcClient.clientStreaming("Join", responseStream); // (3) + grpcClient.clientStreaming("Join", responseStream); // <3> for (String word : sentence.split(" ")) { - clientStream.onNext(StringMessage.newBuilder().setText(word).build()); // (4) + clientStream.onNext(StringMessage.newBuilder().setText(word).build()); // <4> } - clientStream.onCompleted(); // (5) + clientStream.onCompleted(); // <5> } public static class StringMessageStream { /* code imitted */ } @@ -236,21 +251,17 @@ public class ProtoBasedStringServiceClient { } ---- -1. We prepare the collection that contains the values to be streamed. - -2. We call the first variant of the `clientStreaming()` method that takes the +<1> We prepare the collection that contains the values to be streamed. +<2> We call the first variant of the `clientStreaming()` method that takes the method name and the collection of values to be streamed from the client. Note: The above helper method is useful if the values to be streamed is fixed and small in number. - -3. If the number of values to be streamed is large (or unknown), then it is better to use this +<3> If the number of values to be streamed is large (or unknown), then it is better to use this variant of the `clientStreaming()` method that takes a `io.grpc.StreamObserver` as an argument. This method returns a client stream through which the client can stream (potentially a large number of) value to the server. - -4. Once the client stream is obtained, the client streams the values using the `onNext()` method on the +<4> Once the client stream is obtained, the client streams the values using the `onNext()` method on the stream. - -5. When all values have been stream, the client invokes the `onCompleted()` method signal that all values +<5> When all values have been stream, the client invokes the `onCompleted()` method signal that all values have been streamed from the client. === Invoking a server streaming method on the StringService (generated from `protoc`) @@ -267,10 +278,10 @@ public class ProtoBasedStringServiceClient { public void invokeServerStreaming() throws Exception { String sentence = "This sentence will be split into words and sent back to client"; - StringMessage input = StringMessage.newBuilder().setText(sentence).build(); // (1) + StringMessage input = StringMessage.newBuilder().setText(sentence).build(); // <1> - StringMessageStream observer = new StringMessageStream<>(); // (2) - grpcClient.serverStreaming("Split", input, observer); // (3) + StringMessageStream observer = new StringMessageStream<>(); // <2> + grpcClient.serverStreaming("Split", input, observer); // <3> } public static class StringMessageStream { /* code imitted */ } @@ -278,11 +289,9 @@ public class ProtoBasedStringServiceClient { } ---- -1. We prepare the input `StringMessage` that needs to be split. - -2. We create a `StringMessageStream` which will receive the results streamed from the server. - -3. We call the `serverStreaming()` passing the input and the `StringMessageStream` as arguments. +<1> We prepare the input `StringMessage` that needs to be split. +<2> We create a `StringMessageStream` which will receive the results streamed from the server. +<3> We call the `serverStreaming()` passing the input and the `StringMessageStream` as arguments. The server sends a stream of words by calling the `onNext()` method on the `StringMessageStream` for each word. @@ -301,15 +310,15 @@ public class ProtoBasedStringServiceClient { public void invokeBidiStreaming() throws Exception { - StringMessageStream observer = new StringMessageStream<>(); // (1) + StringMessageStream observer = new StringMessageStream<>(); // <1> StringMessageStream clientStream = grpcClient - .bidiStreaming("Echo", observer); // (2) + .bidiStreaming("Echo", observer); // <2> String sentence = "Each word will be echoed back to the client by the server"; for (String word : sentence.split(" ")) { - clientStream.onNext(StringMessage.newBuilder().setText(word).build()); // (3) + clientStream.onNext(StringMessage.newBuilder().setText(word).build()); // <3> } - clientStream.onCompleted(); // (4) + clientStream.onCompleted(); // <4> } public static class StringMessageStream { /* code imitted */ } @@ -317,20 +326,17 @@ public class ProtoBasedStringServiceClient { } ---- -1. We create a `StringMessageStream` which will receive the results streamed from the server. - -2. We call the `bidiStreaming()` passing the `observer` as argument. The server will +<1> We create a `StringMessageStream` which will receive the results streamed from the server. +<2> We call the `bidiStreaming()` passing the `observer` as argument. The server will send its results through this stream (basically by calling the `onNext()` on the `observer`). The method returns a (client) stream which should be used by the client to stream values to the server. - -3. We stream each word in our sentence to the server by calling the `onNext()` method on the +<3> We stream each word in our sentence to the server by calling the `onNext()` method on the `clientStream`. - -4. We call the `onCompleted()` method on the `clientStream` to signal that the client has +<4> We call the `onCompleted()` method on the `clientStream` to signal that the client has streamed all its values. -== Programmatically creating ClientServiceDescriptor for StringService +=== Programmatically creating ClientServiceDescriptor for StringService Assuming that the service is still running on port 1408, lets see how to create our Client without using the `StringService` 's proto `ServiceDescriptor`. @@ -351,30 +357,30 @@ public class StringServiceClient { public static void main(String[] args) { ClientMethodDescriptor lower = ClientMethodDescriptor - .unary("StringService", "Lower") // (1) - .requestType(StringMessage.class) // (2) - .responseType(StringMessage.class) // (3) - .build(); // (4) + .unary("StringService", "Lower") // <1> + .requestType(StringMessage.class) // <2> + .responseType(StringMessage.class) // <3> + .build(); // <4> ClientMethodDescriptor join = ClientMethodDescriptor - .clientStreaming("StringService", "Join") // (5) + .clientStreaming("StringService", "Join") // <5> .requestType(StringMessage.class) .responseType(StringMessage.class) .build(); ClientMethodDescriptor split = ClientMethodDescriptor - .serverStreaming("StringService", "Split") // (6) + .serverStreaming("StringService", "Split") // <6> .requestType(StringMessage.class) .responseType(StringMessage.class) .build(); ClientMethodDescriptor echo = ClientMethodDescriptor - .bidirectional("StringService", "Echo") // (7) + .bidirectional("StringService", "Echo") // <7> .requestType(StringMessage.class) .responseType(StringMessage.class) .build(); - ClientServiceDescriptor serviceDesc = ClientServiceDescriptor // (8) + ClientServiceDescriptor serviceDesc = ClientServiceDescriptor // <8> .builder(StringService.class) .unary(lower) .clientStreaming(join) @@ -383,65 +389,56 @@ public class StringServiceClient { .build(); - Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // (9) + Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // <9> .usePlaintext().build(); - GrpcServiceClient client = GrpcServiceClient.create(channel, serviceDesc); // (10) + GrpcServiceClient client = GrpcServiceClient.create(channel, serviceDesc); // (<10> } } ---- -1. Use the `unary()` method on `ClientMethodDescriptor` to create a builder for a gRPC unary method. +<1> Use the `unary()` method on `ClientMethodDescriptor` to create a builder for a gRPC unary method. The service name and the method name ("Lower") are specified. - -2. Set the request type of the method to be `StringMessage` (since the `Lower` method takes `StringMessage` as a parameter). - -3. Set the response type of the method to be `StringMessage` (since the `Lower` method returns a `StringMessage` as a parameter). - -4. Build the `ClientMethodDescriptor`. Note that the return value is a `ClientMethodDescriptor` that contains +<2> Set the request type of the method to be `StringMessage` (since the `Lower` method takes `StringMessage` as a parameter). +<3> Set the response type of the method to be `StringMessage` (since the `Lower` method returns a `StringMessage` as a parameter). +<4> Build the `ClientMethodDescriptor`. Note that the return value is a `ClientMethodDescriptor` that contains the correct Marshallers for the request & response types. - -5. Use the `clientStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC client streaming method. +<5> Use the `clientStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC client streaming method. The service name and the method name ("Join") are specified. - -6. Use the `serverStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC server streaming method. +<6> Use the `serverStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC server streaming method. The service name and the method name ("Split") are specified. - -7. Use the `bidirectional()` method on `ClientMethodDescriptor` to create a builder for a gRPC Bidi streaming method. +<7> Use the `bidirectional()` method on `ClientMethodDescriptor` to create a builder for a gRPC Bidi streaming method. The service name and the method name ("Echo") are specified. - -8. Create a `ClientServiceDescriptor` for service named `StringService` and add all our `ClientMethodDescriptor` s. - -9. We create a `Channel` to the service that is running on `localhost:1408`. - -10. Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor` +<8> Create a `ClientServiceDescriptor` for service named `StringService` and add all our `ClientMethodDescriptor` s. +<9> We create a `Channel` to the service that is running on `localhost:1408`. +<10> Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor` and `Channel`. At this point the `client` object can be used to invoke any of the four types of methods we have seen in the -earlier sections!! +earlier sections. -== Creating gRPC clients for non protobuf services +=== Creating gRPC clients for non protobuf services If your service is *not* using protobuf for serialization, then the Client framework allows you to programmatically initialize `ClientMethodDescriptor` and create clients to invoke methods on the service. -All you have to do is create the set of `ClientMethodDescriptor` s and the `ClientServiceDescriptor` as -described in the previous section, but with one change. Just *do not* to set the request and response types +All you have to do is create the set of `ClientMethodDescriptor's` and the `ClientServiceDescriptor` as +described in the previous section, but with one change. Just *do not* set the request and response types in the `ClientMethodDescriptor`. That's all!! In fact, there is an API in the `ClientServiceDescriptor` that makes this even simpler. You can simply pass the method name. For example, to create a client streaming -method called "JoinString" that uses java serialization simply call the `clientStreamin("JoinString")`. +method called `"JoinString"` that uses java serialization, simply call the `clientStreaming("JoinString")`. Lets see an example of creating a client for a service that uses Java serialization. [source,java] ---- public static void main(String[] args) throws Exception { - ClientServiceDescriptor descriptor = ClientServiceDescriptor.builder(HelloService.class) // (1) - .clientStreaming("JoinString") // (2) + ClientServiceDescriptor descriptor = ClientServiceDescriptor.builder(HelloService.class) // <1> + .clientStreaming("JoinString") // <2> .build(); Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) @@ -459,12 +456,84 @@ public static void main(String[] args) throws Exception { } ---- -1. Create a `ClientServiceDescriptor` for the `HelloService`. -2. Add the "JoinString" client streaming method to the `ClientServiceDescriptor`. Since we didn't set +<1> Create a `ClientServiceDescriptor` for the `HelloService`. +<2> Add the "JoinString" client streaming method to the `ClientServiceDescriptor`. Since we didn't set the request or response type (like we did in the previous sections), Java serialization will be used for Marshalling and Unmarshalling the request and response values. Note that whether a `ClientServiceDescriptor` is built using protobuf artifacts or is built programmatically, the same set of APIs provided by the Client Framework can be used to invoke gRPC methods. +include::marshalling.adoc[leveloffset=2] + +== Configuration +Configure the gRPC client using the Helidon configuration framework, either programmatically or via a configuration file. +As mentioned earlier, creating a `GrpcServiceClient` involves: + +1. Creating a `ClientServiceDescriptor` which describes the methods in the service that this client can invoke. +2. Creating a gRPC `Channel` through which the client communicates with the server. + +=== Configuring the ClientServiceDescriptor + +The only way to configure the `ClientServiceDescriptor` is in your application code. + +[source,java] +---- +ClientServiceDescriptor descriptor = ClientServiceDescriptor + .builder(HelloService.class) // <1> + .unary("SayHello") // <2> + .build(); // <3> +---- + +<1> Create a builder for a `ClientServiceDescriptor` for the `HelloService`. +<2> Specify that the `HelloService` has a unary method named `SayHello`. There are many other methods in this class that allow you +to define `ClientStreaming`, `ServerStreaming` and `Bidirectional` methods. +<3> Build the `ClientServiceDescriptor`. + +=== Configuring the gRPC Channel + +gRPC allows various channel configurations (deadlines, retries, interceptors etc.) + +Please refer to gRPC documentation: https://grpc.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html. + +== Examples +=== Quick Start + +First, create and run a minimalist `HelloService` gRPC server application as described in the +xref:server.adoc#_quick_start[gRPC Server quick start example]. + +Assuming that the server is running on port 1408, create a client as follows: + +[source,java] +---- +public static void main(String[] args) throws Exception { + ClientServiceDescriptor descriptor = ClientServiceDescriptor.builder(HelloService.class) // <1> + .marshallerSupplier(new JavaMarshall.Supplier()) // <2> + .unary("SayHello") // <3> + .build(); + + Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408) // <4> + .usePlaintext() + .build(); + + GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor); // <5> + + CompletionStage future = client.unary("SayHello", "Helidon gRPC!!"); // <6> + System.out.println(future.get()); // <7> + +} +---- +<1> Create a `ClientServiceDescriptor` for the `HelloService`. +<2> Specify custom marshaller using Java serialization to marshall requests and responses. +<3> Add the `SayHello` unary method to the `ClientServiceDescriptor`. This method, by default, uses Java serialization for +marshalling and unmarshalling the request and response values. +<4> Create a gRPC `Channel` that will communicate with the server running in localhost and on port 1408 (using plaintext). +<5> Create the `GrpcServiceClient` that uses the above `Channel` and `ClientServiceDescriptor`. `GrpcClientService` represents +a client that can be used to define the set of methods described by the specified `ClientServiceDescriptor`. In our case, the +`ClientServiceDescriptor` defines one unary method called `SayHello`. +<6> Invoke the `SayHello` method which returns a `CompletionStage`. +<7> Print the result. + +The example above creates a very simple client to the gRPC server that by default uses Java serialization to marshall +requests and responses. diff --git a/docs/se/grpc/configuration.adoc b/docs/se/grpc/configuration.adoc deleted file mode 100644 index 0f0687691c6..00000000000 --- a/docs/se/grpc/configuration.adoc +++ /dev/null @@ -1,69 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Server Configuration -:description: Helidon gRPC Server Configuration -:keywords: helidon, grpc, java, configuration -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Configure the gRPC Server using the Helidon configuration framework, either programmatically -or via a configuration file. - -== Configuring the gRPC Server in your code - -The easiest way to configure the gRPC Server is in your application code. - -[source,java] ----- -GrpcServerConfiguration configuration = GrpcServerConfiguration.builder() - .port(8080) - .build(); -GrpcServer grpcServer = GrpcServer.create(configuration, routing); ----- - -== Configuring the gRPC Server in a configuration file - -You can also define the configuration in a file. - -[source,hocon] -.GrpcServer configuration file `application.yaml` ----- -grpcserver: - port: 3333 ----- - -Then, in your application code, load the configuration from that file. - -[source,java] -.GrpcServer initialization using the `application.conf` file located on the classpath ----- -GrpcServerConfiguration configuration = GrpcServerConfiguration.create( - Config.builder() - .sources(classpath("application.conf")) - .build()); - -GrpcServer grpcServer = GrpcServer.create(configuration, routing); ----- - -== Configuration options - -See all configuration options - link:{javadoc-base-url}/io.helidon.grpc/GrpcServerConfiguration.html[here]. - diff --git a/docs/se/grpc/health-checks.adoc b/docs/se/grpc/health-checks.adoc deleted file mode 100644 index 9ca934fd07b..00000000000 --- a/docs/se/grpc/health-checks.adoc +++ /dev/null @@ -1,119 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Service Health Checks -:description: Helidon gRPC Service Health Checks -:keywords: helidon, grpc, java -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== Service Health Checks - -Helidon gRPC services provide a built-in support for Helidon Health Checks. - -Unless a custom health check is implemented by the service developer, each service -deployed to the gRPC server will be provisioned with a default health check, which -always returns status of `UP`. - -This allows all services, including the ones that don't have a meaningful health check, -to show up in the health report (or to be queried for health) without service developer -having to do anything. - -However, services that do need custom health checks can easily define one, -directly within `GrpcService` implementation: - -[source,java] ----- -public class MyService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.unary("MyMethod", this::myMethod) - .healthCheck(this::healthCheck); // <1> - } - - private HealthCheckResponse healthCheck() { - boolean fUp = isMyServiceUp(); // <2> - return HealthCheckResponse - .named(name()) // <3> - .state(fUp) // <4> - .withData("ts", System.currentTimeMillis()) // <5> - .build(); - } - - private void myMethod(ReqT request, StreamObserver observer) { - // do something - } -} ----- - -<1> Configure a custom health check for the service -<2> Determine service status -<3> Use service name as a health check name for consistency -<4> Use determined service status -<5> Optionally, provide additional metadata - -You can also define custom health check for an existing service, including plain -`io.grpc.BindableService` implementations, using service configurer inside the -`GrpcRouting` deefinition: - -[source,java] ----- -private static GrpcRouting createRouting() { - return GrpcRouting.builder() - .register(new EchoService(), cfg -> cfg.healthCheck(MyCustomHealthChecks::echoHealthCheck)) // <1> - .build(); -} ----- - -<1> Configure custom health check for an existing or legacy service - -== Exposing Health Checks - -All gRPC service health checks are managed by the Helidon gRPC Server, and are -automatically exposed to the gRPC clients using custom implementation of the -standard gRPC `HealthService` API. - -However, they can also be exposed to REST clients via standard Helidon/Microprofile -`/health` endpoint: - -[source,java] ----- - GrpcServer grpcServer = GrpcServer.create(grpcServerConfig(), createRouting(config)); // <1> - grpcServer.start(); // <2> - - HealthSupport health = HealthSupport.builder() - .add(grpcServer.healthChecks()) // <3> - .build(); - - Routing routing = Routing.builder() - .register(health) // <4> - .build(); - - WebServer.create(webServerConfig(), routing).start(); // <5> ----- - -<1> Create `GrpcServer` instance -<2> Start gRPC server, which will deploy all services and register default and custom health checks -<3> Add gRPC server managed health checks to `HealthSupport` instance -<4> Add `HealthSupport` to the web server routing definition -<5> Create and start web server - -All gRPC health checks will now be available via `/health` REST endpoint, in -addition to the standard gRPC `HealthService` diff --git a/docs/se/grpc/interceptors.adoc b/docs/se/grpc/interceptors.adoc deleted file mode 100644 index 3d97c096247..00000000000 --- a/docs/se/grpc/interceptors.adoc +++ /dev/null @@ -1,126 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Interceptors -:description: Helidon gRPC Service Interceptors -:keywords: helidon, grpc, java -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== Interceptors - -Helidon gRPC allows you to configure standard `io.grpc.ServerInterceptor`s. - -For example, you could implement an interceptor that logs each RPC call: - -[source,java] ----- -class LoggingInterceptor implements ServerInterceptor { // <1> - - private static final Logger LOG = Logger.getLogger(LoggingInterceptor.class.getName()); - - @Override - public ServerCall.Listener interceptCall(ServerCall call, - Metadata metadata, - ServerCallHandler handler) { - - LOG.info(() -> "CALL: " + call.getMethodDescriptor()); // <2> - return handler.startCall(call, metadata); // <3> - } -} ----- - -<1> Implement `io.grpc.ServerInterceptor` -<2> Implement the logging logic -<3> Start intercepted call - -== Registering Interceptors - -You can register interceptors globally, in which case they will be applied to all -methods of all services, by simply adding them to the `GrpcRouting` instance: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .intercept(new LoggingInterceptor()) // <1> - .register(new GreetService(config)) - .register(new EchoService()) - .build(); -} ----- - -<1> Adds `LoggingInterceptor` to all methods of `GreetService` and `EchoService` - -You can also register an interceptor for a specific service, either by implementing -`GrpcService.update` method: - -[source,java] ----- -public class MyService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.intercept(new LoggingInterceptor()) // <1> - .unary("MyMethod", this::myMethod); - } - - private void myMethod(ReqT request, StreamObserver observer) { - // do something - } -} ----- - -<1> Adds `LoggingInterceptor` to all methods of `MyService` - -Or by configuring `ServiceDescriptor` externally, when creating `GrpcRouting`, which -allows you to add interceptors to plain `io.grpc.BindableService` services as well: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .register(new GreetService(config), cfg -> cfg.intercept(new LoggingInterceptor())) // <1> - .register(new EchoService()) - .build(); -} ----- - -<1> Adds `LoggingInterceptor` to all methods of `GreetService` only - -Finally, you can also register an interceptor at the method level: - -[source,java] ----- -public class MyService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.unary("MyMethod", - this::myMethod, - cfg -> cfg.intercept(new LoggingInterceptor())); // <1> - } - - private void myMethod(ReqT request, StreamObserver observer) { - // do something - } -} ----- - -<1> Adds `LoggingInterceptor` to `MyService::MyMethod` only diff --git a/docs/se/grpc/introduction.adoc b/docs/se/grpc/introduction.adoc deleted file mode 100644 index 2e18bee5bf5..00000000000 --- a/docs/se/grpc/introduction.adoc +++ /dev/null @@ -1,79 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Server Introduction -:description: Helidon gRPC Server Introduction -:keywords: helidon, grpc, java -:feature-name: gRPC -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Helidon gRPC Server provides a framework for creating link:http://grpc.io/[gRPC] applications. - -== Experimental - -WARNING: The Helidon gRPC feature is currently experimental and the APIs are - subject to changes until gRPC support is stabilized. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.grpc - helidon-grpc-server - ----- - -== Quick Start - -Here is the code for a minimalist gRPC application that runs on a default port (1408): - -[source,java] ----- -public static void main(String[] args) throws Exception { - GrpcServer grpcServer = GrpcServer - .create(GrpcRouting.builder() - .register(new HelloService()) // <1> - .build()) - .start() // <2> - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); // Implement the simplest possible gRPC service. // <3> - - System.out.println("gRPC Server started at: http://localhost:" + grpcServer.port()); // <4> -} - -static class HelloService implements GrpcService { <5> - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.unary("SayHello", ((request, responseObserver) -> complete(responseObserver, "Hello " + request))); // <6> - } -} ----- - -<1> Register gRPC service. -<2> Start the server. -<3> Wait for the server to start while throwing possible errors as exceptions. -<4> The server is bound to a default port (1408). -<5> Implement the simplest possible gRPC service. -<6> Add unary method `HelloService/SayHello` to the service definition. - -The example above deploys a very simple service to the gRPC server that by default uses Java serialization to marshall -requests and responses. We will look into deployment of "standard" gRPC services that use Protobuf for request and -response marshalling, as well as how you can configure custom marshallers, later in this document. diff --git a/docs/se/grpc/marshalling.adoc b/docs/se/grpc/marshalling.adoc index 5065da16074..79c6ad254e9 100644 --- a/docs/se/grpc/marshalling.adoc +++ b/docs/se/grpc/marshalling.adoc @@ -18,7 +18,7 @@ = Marshalling :description: Helidon gRPC Marshalling -:keywords: helidon, grpc, java +:keywords: helidon, grpc, java, marshalling :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] diff --git a/docs/se/grpc/metrics.adoc b/docs/se/grpc/metrics.adoc deleted file mode 100644 index 8cb3af1ca9b..00000000000 --- a/docs/se/grpc/metrics.adoc +++ /dev/null @@ -1,196 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Service Metrics -:description: Helidon gRPC Service Metrics -:keywords: helidon, grpc, java -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Helidon gRPC Server has built-in support for metrics capture, which allows -service developers to easily enable application-level metrics for their services. - -== Enabling Metrics Capture - -By default, gRPC Server only captures two vendor-level metrics: `grpc.request.count` -and `grpc.request.meter`. These metrics provide aggregate view of requests across -all services, and serve as an indication of the overall server load. - -However, users can enable more fine grained metrics by simply configuring a built-in -`GrpcMetrics` interceptor within the routing: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .intercept(GrpcMetrics.timed()) // <1> - .register(new GreetService(config)) - .register(new EchoService()) - .build(); -} ----- - -<1> Capture metrics for all methods of all services as a `timer` - -In the example above we have chosen to create and keep a `timer` metric type for -each method of each service. Alternatively, we could've chosen to use a -`counter`, `meter` or a `histogram` instead. - -== Overriding Metrics Capture - -While global metrics capture is certainly useful, it is not always sufficient. -Keeping a separate `timer` for each gRPC method may be an overkill, so the user -could decide to use a lighter-weight metric type, such as `counter` or a `meter`. - -However, she may still want to enable `histogram` or a `timer` for some services, -or even only some methods of some services. - -This can be easily accomplished by overriding the type of the captured metric at -either service or the method level: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .intercept(GrpcMetrics.counted()) // <1> - .register(new MyService()) - .build(); -} - -public static class MyService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules - .intercept(GrpcMetrics.metered()) // <2> - .unary("MyMethod", this::myMethod, - cfg -> cfg.intercept(GrpcMetrics.timer())) // <3> - } - - private void myMethod(ReqT request, StreamObserver observer) { - // do something - } -} ----- - -<1> Use `counter` for all methods of all services, unless overridden -<2> Use `meter` for all methods of `MyService` -<3> Use `timer` for `MyService::MyMethod` - -== Exposing Metrics Externally - -Collected metrics are stored in the standard Helidon Metric Registries, such as vendor and -application registry, and can be exposed via standard `/metrics` REST API. - -[source,java] ----- -Routing routing = Routing.builder() - .register(MetricsSupport.create()) // <1> - .build(); - -WebServer.create(webServerConfig(), routing) // <2> - .start() ----- -<1> Add `MetricsSupport` instance to web server routing -<2> Create and start Helidon web server - -See xref:../metrics/metrics.adoc[Helidon Metrics] documentation for more details. - -== Specifying Metric Meta-data - -Helidon metrics contain meta-data such as tags, a description, units etc. It is possible to -add this additional meta-data when specifying the metrics. - -=== Adding Tags - -To add tags to a metric a `Map` of key/value tags can be supplied. -For example: -[source,java] ----- -Map tagMap = new HashMap<>(); -tagMap.put("keyOne", "valueOne"); -tagMap.put("keyTwo", "valueTwo"); - -GrpcRouting routing = GrpcRouting.builder() - .intercept(GrpcMetrics.counted().tags(tagMap)) // <1> - .register(new MyService()) - .build(); ----- -<1> the `tags()` method is used to add the `Map` of tags to the metric. - -=== Adding a Description - -A meaningful description can be added to a metric: -For example: -[source,java] ----- -GrpcRouting routing = GrpcRouting.builder() - .intercept(GrpcMetrics.counted().description("Something useful")) // <1> - .register(new MyService()) - .build(); ----- - -<1> the `description()` method is used to add the description to the metric. - -=== Adding Metric Units - -A units value can be added to the Metric: -For example: -[source,java] ----- -GrpcRouting routing = GrpcRouting.builder() - .intercept(GrpcMetrics.timed().units(MetricUnits.SECONDS)) // <1> - .register(new MyService()) - .build(); ----- -<1> the `units()` method is used to add the metric units to the metric. -Typically the units value is one of the constants from `org.eclipse.microprofile.metrics.MetricUnits` class. - -== Overriding the Metric Name - -By default the metric name is the gRPC service name followed by a dot ('.') followed by the method name. -It is possible to supply a function that can be used to override the default behaviour. - -The function should implement the `io.helidon.grpc.metrics.GrpcMetrics.NamingFunction` interface -[source,java] ----- -@FunctionalInterface -public interface NamingFunction { - /** - * Create a metric name. - * - * @param service the service descriptor - * @param methodName the method name - * @param metricType the metric type - * @return the metric name - */ - String createName(ServiceDescriptor service, String methodName, MetricType metricType); -} ----- -This is a functional interface so lambda can be used too. - -For example: -[source,java] ----- -GrpcRouting routing = GrpcRouting.builder() - .intercept(GrpcMetrics.counted() - .nameFunction((svc, method, metric) -> "grpc." + service.name() + '.' + method) // <1> ----- -<1> the `NamingFunction` is just a lambda that returns the concatenated service name and method name -with the prefix `grpc.` So for a service "Foo", method "bar" the above example would produce a name "grpc.Foo.bar". diff --git a/docs/se/grpc/routing.adoc b/docs/se/grpc/routing.adoc deleted file mode 100644 index 4d64f3255ab..00000000000 --- a/docs/se/grpc/routing.adoc +++ /dev/null @@ -1,104 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Server Routing -:description: Helidon gRPC Server Routing -:keywords: helidon, grpc, java -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== gRPC Server Routing - -Unlike Webserver, which allows you to route requests based on path expression -and the HTTP verb, gRPC server always routes requests based on the service and -method name. This makes routing configuration somewhat simpler -- all you need -to do is register your services: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .register(new GreetService(config)) // <1> - .register(new EchoService()) // <2> - .register(new MathService()) // <3> - .build(); -} ----- - -<1> Register `GreetService` instance. -<2> Register `EchoService` instance. -<3> Register `MathService` instance. - -Both "standard" gRPC services that implement `io.grpc.BindableService` interface -(typically implemented by extending generated server-side stub and overriding -its methods), and Helidon gRPC services that implement -`io.helidon.grpc.server.GrpcService` interface can be registered. - -The difference is that Helidon gRPC services allow you to customize behavior -down to the method level, and provide a number of useful helper methods that -make service implementation easier, as we'll see in a moment. - -== Customizing Service Definitions - -When registering a service, regardless of its type, you can customize its -descriptor by providing configuration consumer as a second argument to the -`register` method. - -This is particularly useful when registering standard `BindableService` -instances, as it allows you to add certain Helidon-specific behaviors, such as -xref:health-checks.adoc[health checks] and xref:metrics.adoc[metrics] to them: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .register(new GreetService(config)) - .register(new EchoService(), service -> { - service.healthCheck(CustomHealthChecks::echoHealthCheck) // <1> - .metered(); // <2> - }) - .build(); -} ----- - -<1> Add custom health check to the service. -<2> Specify that all the calls to service methods should be metered. - -== Specifying Global Interceptors - -`GrpcRouting` also allows you to specify xref:interceptors.adoc[custom interceptors] -that will be applied to all registered services. - -This is useful to configure features such as tracing, security and metrics collection, -and we provide built-in interceptors for those purposes that you can simply register -with the routing definition: - -[source,java] ----- -private static GrpcRouting createRouting(Config config) { - return GrpcRouting.builder() - .intercept(GrpcMetrics.timed()) // <1> - .register(new GreetService(config)) - .register(new EchoService()) - .register(new MathService()) - .build(); -} ----- - -<1> Register `GrpcMetrics` interceptor that will collect timers for all methods of all services (but can be overridden at the individual service or even method level). diff --git a/docs/se/grpc/security.adoc b/docs/se/grpc/security.adoc deleted file mode 100644 index 1b1cb24c96a..00000000000 --- a/docs/se/grpc/security.adoc +++ /dev/null @@ -1,233 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Server Security -:description: Helidon Security gRPC integration -:keywords: helidon, grpc, security -:feature-name: gRPC Server Security -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -Security integration of the xref:introduction.adoc[gRPC server] - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.security.integration - helidon-security-integration-grpc - ----- - -== Bootstrapping - -There are two steps to configure security with gRPC server: - -1. Create security instance and register it with server -2. Protect gRPC services of server with various security features - -[source,java] -.Example using builders ----- -// gRPC server's routing -GrpcRouting.builder() - // This is step 1 - register security instance with gRPC server processing - // security - instance of security either from config or from a builder - // securityDefaults - default enforcement for each service that has a security definition - .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate())) - // this is step 2 - protect a service - // register and protect this service with authentication (from defaults) and role "user" - .register(greetService, GrpcSecurity.rolesAllowed("user")) - .build(); ----- - -[source,java] -.Example using builders for more fine grained method level security ----- -// create the service descriptor -ServiceDescriptor greetService = ServiceDescriptor.builder(new GreetService()) - // Add an instance of gRPC security that will apply to all methods of - // the service - in this case require the "user" role - .intercept(GrpcSecurity.rolesAllowed("user")) - // Add an instance of gRPC security that will apply to the "SetGreeting" - // method of the service - in this case require the "admin" role - .intercept("SetGreeting", GrpcSecurity.rolesAllowed("admin")) - .build(); - -// Create the gRPC server's routing -GrpcRouting.builder() - // This is step 1 - register security instance with gRPC server processing - // security - instance of security either from config or from a builder - // securityDefaults - default enforcement for each service that has a security definition - .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate())) - // this is step 2 - add the service descriptor - .register(greetService) - .build(); ----- - -[source,java] -.Example using configuration ----- -GrpcRouting.builder() - // helper method to load both security and gRPC server security from configuration - .intercept(GrpcSecurity.create(config)) - // continue with gRPC server route configuration... - .register(new GreetService()) - .build(); ----- - -[source,conf] -.Example using configuration - configuration (HOCON) ----- -# This may change in the future - to align with gRPC server configuration, -# once it is supported -security - grpc-server: - # Configuration of integration with gRPC server - defaults: - authenticate: true - # Configuration security for individual services - services: - - name: "GreetService" - defaults: - roles-allowed: ["user"] - # Configuration security for individual methods of the service - methods: - - name: "SetGreeting" - roles-allowed: ["admin"] ----- - -=== Client security -When using the Helidon SE gRPC client API security can be configured for a gRPC service -or at the individual method level. The client API has a custom `CallCredentials` implementation that -integrates with the Helidon security APIs. - -[source,java] -.Example configuring client security for a service ----- -Security security = Security.builder() // <1> - .addProvider(HttpBasicAuthProvider.create(config.get("http-basic-auth"))) - .build(); - -GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <2> - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user) - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password) - .build(); - -ClientServiceDescriptor descriptor = ClientServiceDescriptor // <3> - .builder(StringService.class) - .unary("Lower") - .callCredentials(clientSecurity) // <4> - .build(); - -GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor); // <5> - -String response = client.blockingUnary("Lower", "ABCD"); // <6> ----- -<1> Create the Helidon `Security` instance (in this case using the basic auth provider) -<2> Create the `GrpcClientSecurity` gRPC `CallCredentials` adding the user and password -property expected by the basic auth provider. -<3> Create the gRPC `ClientServiceDescriptor` for the `StringService` gRPC service. -<4> Set the `GrpcClientSecurity` instance as the call credentials for all methods of the service -<5> Create a `GrpcServiceClient` that will allow methods to be called on the service -<6> Call the "Lower" method which will use the configured basic auth credentials - - -[source,java] -.Example configuring client security for a specific method ----- -GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <1> - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user) - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password) - .build(); - -ClientServiceDescriptor descriptor = ClientServiceDescriptor // <2> - .builder(StringService.class) - .unary("Lower") - .unary("Upper", rules -> rules.callCredentials(clientSecurity)) // <3> - .build(); ----- -<1> Create the `GrpcClientSecurity` call credentials in the same way as above. -<2> Create the `ClientServiceDescriptor`, this time with two unary methods, "Lower" and "Upper". -<3> The "Upper" method is configured to use the `GrpcClientSecurity` call credentials, the "Lower" method -will be called without any credentials. - - -=== Outbound security -Outbound security covers three scenarios: - -* Calling a secure gRPC service from inside a gRPC service method handler -* Calling a secure gRPC service from inside a web server method handler -* Calling a secure web endpoint from inside a gRPC service method handler - -Within each scenario credentials can be propagated if the gRPC/http method -handler is executing within a security context or credentials can be overridden -to provide a different set of credentials to use to call the outbound endpoint. - -[source,java] -.Example calling a secure gRPC service from inside a gRPC service method handler ----- -// Obtain the SecurityContext from the current gRPC call Context -SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get(); - -// Create a gRPC CallCredentials that will use the current request's -// security context to configure outbound credentials -GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(securityContext); - -// Create the gRPC stub using the CallCredentials -EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity); ----- - -[source,java] -.Example calling a secure gRPC service from inside a web server method handler ----- -private static void propagateCredentialsWebRequest(ServerRequest req, ServerResponse res) { - try { - // Create a gRPC CallCredentials that will use the current request's - // security context to configure outbound credentials - GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(req); - - // Create the gRPC stub using the CallCredentials - EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity); - - String message = req.queryParams().first("message").orElse(null); - Echo.EchoResponse echoResponse = stub.echo(Echo.EchoRequest.newBuilder().setMessage(message).build()); - res.send(echoResponse.getMessage()); - } catch (StatusRuntimeException e) { - res.status(GrpcHelper.toHttpResponseStatus(e)).send(); - } catch (Throwable thrown) { - res.status(Http.ResponseStatus.create(500, thrown.getMessage())).send(); - } -} ----- - -[source,java] -.Example calling a secure web endpoint from inside a gRPC service method handler ----- -// Obtain the SecurityContext from the gRPC call Context -SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get(); - -// Use the SecurityContext as normal to make a http request -Response webResponse = client.target(url) - .path("/test") - .request() - .property(ClientSecurity.PROPERTY_CONTEXT, securityContext) - .get(); ----- diff --git a/docs/se/grpc/server.adoc b/docs/se/grpc/server.adoc new file mode 100644 index 00000000000..df6e4a9b89b --- /dev/null +++ b/docs/se/grpc/server.adoc @@ -0,0 +1,933 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2019, 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. + +/////////////////////////////////////////////////////////////////////////////// + += gRPC Server +:description: Helidon gRPC Server +:keywords: helidon, grpc, java, se +:feature-name: gRPC Server +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/se.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> + +== Overview +Helidon gRPC Server provides a framework for creating link:http://grpc.io/[gRPC] applications. +While it allows you to deploy any standard gRPC service that +implements `io.grpc.BindableService` interface, including services generated +from the Protobuf IDL files (and even allows you to customize them to a certain +extent), using Helidon gRPC framework to implement your services has a number of +benefits: + +* It allows you to define both HTTP and gRPC services using similar programming +model, simplifying learning curve for developers. + +* It provides a number of helper methods that make service implementation +significantly simpler. + +* It allows you to configure some of the Helidon value-added features, such +as <> and <> +down to the method level. + +* It allows you to easily specify custom marshaller for requests and +responses if Protobuf does not satisfy your needs. + +* It provides built in support for <>. + + +=== Experimental + +WARNING: The Helidon gRPC feature is currently experimental and the APIs are +subject to changes until gRPC support is stabilized. + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.grpc + helidon-grpc-server + +---- + +[[security_maven_coordinartes]] +If `gRPC server security` is required as described in this <> , add the following dependency to your project’s pom.xml: +[source,xml] +---- + + io.helidon.security.integration + helidon-security-integration-grpc + +---- + +== Usage +=== gRPC Server Routing + +Unlike Webserver, which allows you to route requests based on path expression +and the HTTP verb, gRPC server always routes requests based on the service and +method name. This makes routing configuration somewhat simpler -- all you need +to do is register your services: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .register(new GreetService(config)) // <1> + .register(new EchoService()) // <2> + .register(new MathService()) // <3> + .build(); +} +---- + +<1> Register `GreetService` instance. +<2> Register `EchoService` instance. +<3> Register `MathService` instance. + +Both "standard" gRPC services that implement `io.grpc.BindableService` interface +(typically implemented by extending generated server-side stub and overriding +its methods), and Helidon gRPC services that implement +`io.helidon.grpc.server.GrpcService` interface can be registered. + +The difference is that Helidon gRPC services allow you to customize behavior +down to the method level, and provide a number of useful helper methods that +make service implementation easier, as we'll see in a moment. + +==== Customizing Service Definitions + +When registering a service, regardless of its type, you can customize its +descriptor by providing configuration consumer as a second argument to the +`register` method. + +This is particularly useful when registering standard `BindableService` +instances, as it allows you to add certain Helidon-specific behaviors, such as +<> and <> to them: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .register(new GreetService(config)) + .register(new EchoService(), service -> { + service.healthCheck(CustomHealthChecks::echoHealthCheck) // <1> + .metered(); // <2> + }) + .build(); +} +---- + +<1> Add custom health check to the service. +<2> Specify that all the calls to service methods should be metered. + +==== Specifying Global Interceptors + +`GrpcRouting` also allows you to specify <> that will be applied to all registered services. + +This is useful to configure features such as tracing, security and metrics collection, +and we provide built-in interceptors for those purposes that you can simply register +with the routing definition: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .intercept(GrpcMetrics.timed()) // <1> + .register(new GreetService(config)) + .register(new EchoService()) + .register(new MathService()) + .build(); +} +---- + +<1> Register `GrpcMetrics` interceptor that will collect timers for all methods of all services (but can be overridden at the individual service or even method level). + +=== Service Implementation + +At the very basic level, all you need to do in order to implement a Helidon +gRPC service is create a class that implements `io.helidon.grpc.server.GrpcService` +interface and define one or more methods for the service: + +[source,java] +---- +class EchoService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.unary("Echo", this::echo); // <1> + } + + /** + * Echo the message back to the caller. + * + * @param request the echo request containing the message to echo + * @param observer the response observer + */ + public void echo(String request, StreamObserver observer) { // <2> + complete(observer, request); // <3> + } +} +---- + +<1> Define unary method `Echo` and map it to the `this::echo` handler. +<2> Create a handler for the `Echo` method. +<3> Send the request string back to the client by completing response observer. + +NOTE: The `complete` method shown in the example above is just one of many helper +methods available in the `GrpcService` class. See the full list +link:{grpc-server-javadoc-base-url}/io/helidon/grpc/server/GrpcService.html[here]. + +The example above implements a service with a single unary method, which will be +exposed at the `EchoService/Echo' endpoint. The service does not explicitly define +a marshaller for requests and responses, so Java serialization will be used as a +default. + +Unfortunately, this implies that you will have to implement clients by hand and +configure them to use the same marshaller as the server. Obviously, one of the +major selling points of gRPC is that it makes it easy to generate clients for a +number of languages (as long as you use Protobuf for marshalling), so let's see +how we would implement Protobuf enabled Helidon gRPC service. + +==== Implementing Protobuf Services + +In order to implement Protobuf-based service, you would follow the official +link:https://grpc.io/docs/quickstart/java.html[instructions] on the gRPC +web site, which boil down to the following: + +===== Define the Service IDL + +For this example, we will re-implement the `EchoService` above as a Protobuf +service in `echo.proto` file. + +[source, proto] +---- +syntax = "proto3"; +option java_package = "org.example.services.echo"; + +service EchoService { + rpc Echo (EchoRequest) returns (EchoResponse) {} +} + +message EchoRequest { + string message = 1; +} + +message EchoResponse { + string message = 1; +} +---- + +Based on this IDL, the gRPC compiler will generate message classes (`EchoRequest` +and `EchoResponse`), client stubs that can be used to make RPC calls to the server, +as well as the base class for the server-side service implementation. + +We can ignore the last one, and implement the service using Helidon gRPC framework +instead. + +===== Implement the Service + +The service implementation will be very similar to our original implementation: + +[source,java] +---- +class EchoService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.proto(Echo.getDescriptor()) // <1> + .unary("Echo", this::echo); // <2> + } + + /** + * Echo the message back to the caller. + * + * @param request the echo request containing the message to echo + * @param observer the response observer + */ + public void echo(Echo.EchoRequest request, StreamObserver observer) { // <3> + String message = request.getMessage(); // <4> + Echo.EchoResponse response = Echo.EchoResponse.newBuilder().setMessage(message).build(); // <5> + complete(observer, response); // <6> + } +} +---- + +<1> Specify proto descriptor in order to provide necessary type information and +enable Protobuf marshalling. +<2> Define unary method `Echo` and map it to the `this::echo` handler. +<3> Create a handler for the `Echo` method, using Protobuf message types for request and response. +<4> Extract message string from the request. +<5> Create the response containing extracted message. +<6> Send the response back to the client by completing response observer. + +=== Interceptors + +Helidon gRPC allows you to configure standard interceptors using `io.grpc.ServerInterceptor`. + +For example, you could implement an interceptor that logs each RPC call: + +[source,java] +---- +class LoggingInterceptor implements ServerInterceptor { // <1> + + private static final Logger LOG = Logger.getLogger(LoggingInterceptor.class.getName()); + + @Override + public ServerCall.Listener interceptCall(ServerCall call, + Metadata metadata, + ServerCallHandler handler) { + + LOG.info(() -> "CALL: " + call.getMethodDescriptor()); // <2> + return handler.startCall(call, metadata); // <3> + } +} +---- + +<1> Implement `io.grpc.ServerInterceptor` +<2> Implement the logging logic +<3> Start intercepted call + +==== Registering Interceptors + +You can register interceptors globally, in which case they will be applied to all +methods of all services, by simply adding them to the `GrpcRouting` instance: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .intercept(new LoggingInterceptor()) // <1> + .register(new GreetService(config)) + .register(new EchoService()) + .build(); +} +---- + +<1> Adds `LoggingInterceptor` to all methods of `GreetService` and `EchoService` + +You can also register an interceptor for a specific service, either by implementing +`GrpcService.update` method: + +[source,java] +---- +public class MyService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.intercept(new LoggingInterceptor()) // <1> + .unary("MyMethod", this::myMethod); + } + + private void myMethod(ReqT request, StreamObserver observer) { + // do something + } +} +---- + +<1> Adds `LoggingInterceptor` to all methods of `MyService` + +Or by configuring `ServiceDescriptor` externally, when creating `GrpcRouting`, which +allows you to add interceptors to plain `io.grpc.BindableService` services as well: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .register(new GreetService(config), cfg -> cfg.intercept(new LoggingInterceptor())) // <1> + .register(new EchoService()) + .build(); +} +---- + +<1> Adds `LoggingInterceptor` to all methods of `GreetService` only + +Finally, you can also register an interceptor at the method level: + +[source,java] +---- +public class MyService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.unary("MyMethod", + this::myMethod, + cfg -> cfg.intercept(new LoggingInterceptor())); // <1> + } + + private void myMethod(ReqT request, StreamObserver observer) { + // do something + } +} +---- + +<1> Adds `LoggingInterceptor` to `MyService::MyMethod` only + +=== Service Health Checks + +Helidon gRPC services provide a built-in support for Helidon Health Checks. + +Unless a custom health check is implemented by the service developer, each service +deployed to the gRPC server will be provisioned with a default health check, which +always returns status of `UP`. + +This allows all services, including the ones that don't have a meaningful health check, +to show up in the health report (or to be queried for health) without service developer +having to do anything. + +However, services that do need custom health checks can easily define one, +directly within `GrpcService` implementation: + +[source,java] +---- +public class MyService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.unary("MyMethod", this::myMethod) + .healthCheck(this::healthCheck); // <1> + } + + private HealthC +heckResponse healthCheck() { + boolean fUp = isMyServiceUp(); // <2> + return HealthCheckResponse + .named(name()) // <3> + .state(fUp) // <4> + .withData("ts", System.currentTimeMillis()) // <5> + .build(); + } + + private void myMethod(ReqT request, StreamObserver observer) { + // do something + } +} +---- + +<1> Configure a custom health check for the service +<2> Determine service status +<3> Use service name as a health check name for consistency +<4> Use determined service status +<5> Optionally, provide additional metadata + +You can also define custom health check for an existing service, including plain +`io.grpc.BindableService` implementations, using service configurer inside the +`GrpcRouting` deefinition: + +[source,java] +---- +private static GrpcRouting createRouting() { + return GrpcRouting.builder() + .register(new EchoService(), cfg -> cfg.healthCheck(MyCustomHealthChecks::echoHealthCheck)) // <1> + .build(); +} +---- + +<1> Configure custom health check for an existing or legacy service + +==== Exposing Health Checks + +All gRPC service health checks are managed by the Helidon gRPC Server, and are +automatically exposed to the gRPC clients using custom implementation of the +standard gRPC `HealthService` API. + +However, they can also be exposed to REST clients via standard Helidon/Microprofile +`/health` endpoint: + +[source,java] +---- + GrpcServer grpcServer = GrpcServer.create(grpcServerConfig(), createRouting(config)); // <1> + grpcServer.start(); // <2> + + HealthSupport health = HealthSupport.builder() + .add(grpcServer.healthChecks()) // <3> + .build(); + + Routing routing = Routing.builder() + .register(health) // <4> + .build(); + + WebServer.create(webServerConfig(), routing).start(); // <5> +---- + +<1> Create `GrpcServer` instance +<2> Start gRPC server, which will deploy all services and register default and custom health checks +<3> Add gRPC server managed health checks to `HealthSupport` instance +<4> Add `HealthSupport` to the web server routing definition +<5> Create and start web server + +All gRPC health checks will now be available via `/health` REST endpoint, in +addition to the standard gRPC `HealthService` + +=== Service Metrics +Helidon gRPC Server has built-in support for metrics capture, which allows +service developers to easily enable application-level metrics for their services. + +==== Enabling Metrics Capture + +By default, gRPC Server only captures two vendor-level metrics: `grpc.request.count` +and `grpc.request.meter`.These metrics provide aggregate view of requests across +all services, and serve as an indication of the overall server load. + +However, users can enable more fine grained metrics by simply configuring a built-in +`GrpcMetrics` interceptor within the routing: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .intercept(GrpcMetrics.timed()) // <1> + .register(new GreetService(config)) + .register(new EchoService()) + .build(); +} +---- + +<1> Capture metrics for all methods of all services as a `timer` + +In the example above we have chosen to create and keep a `timer` metric type for +each method of each service. Alternatively, we could've chosen to use a +`counter`, `meter` or a `histogram` instead. + +==== Overriding Metrics Capture + +While global metrics capture is certainly useful, it is not always sufficient. +Keeping a separate `timer` for each gRPC method may be an overkill, so the user +could decide to use a lighter-weight metric type, such as `counter` or a `meter`. + +However, she may still want to enable `histogram` or a `timer` for some services, +or even only some methods of some services. + +This can be easily accomplished by overriding the type of the captured metric at +either service or the method level: + +[source,java] +---- +private static GrpcRouting createRouting(Config config) { + return GrpcRouting.builder() + .intercept(GrpcMetrics.counted()) // <1> + .register(new MyService()) + .build(); +} + +public static class MyService implements GrpcService { + + @Override + public void update(ServiceDescriptor.Rules rules) { + rules + .intercept(GrpcMetrics.metered()) // <2> + .unary("MyMethod", this::myMethod, + cfg -> cfg.intercept(GrpcMetrics.timer())) // <3> + } + + private void myMethod(ReqT request, StreamObserver observer) { + // do something + } +} +---- + +<1> Use `counter` for all methods of all services, unless overridden +<2> Use `meter` for all methods of `MyService` +<3> Use `timer` for `MyService::MyMethod` + +==== Exposing Metrics Externally + +Collected metrics are stored in the standard Helidon Metric Registries, such as vendor and +application registry, and can be exposed via standard `/metrics` REST API. + +[source,java] +---- +Routing routing = Routing.builder() + .register(MetricsSupport.create()) // <1> + .build(); + +WebServer.create(webServerConfig(), routing) // <2> + .start() +---- +<1> Add `MetricsSupport` instance to web server routing +<2> Create and start Helidon web server + +See xref:../metrics/metrics.adoc[Helidon Metrics] documentation for more details. + +==== Specifying Metric Meta-data + +Helidon metrics contain meta-data such as tags, a description, units etc. It is possible to +add this additional meta-data when specifying the metrics. + +===== Adding Tags + +To add tags to a metric a `Map` of key/value tags can be supplied. +For example: +[source,java] +---- +Map tagMap = new HashMap<>(); +tagMap.put("keyOne", "valueOne"); +tagMap.put("keyTwo", "valueTwo"); + +GrpcRouting routing = GrpcRouting.builder() + .intercept(GrpcMetrics.counted().tags(tagMap)) // <1> + .register(new MyService()) + .build(); +---- +<1> the `tags()` method is used to add the `Map` of tags to the metric. + +===== Adding a Description + +A meaningful description can be added to a metric: +For example: +[source,java] +---- +GrpcRouting routing = GrpcRouting.builder() + .intercept(GrpcMetrics.counted().description("Something useful")) // <1> + .register(new MyService()) + .build(); +---- + +<1> the `description()` method is used to add the description to the metric. + +===== Adding Metric Units + +A units value can be added to the Metric: +For example: +[source,java] +---- +GrpcRouting routing = GrpcRouting.builder() + .intercept(GrpcMetrics.timed().units(MetricUnits.SECONDS)) // <1> + .register(new MyService()) + .build(); +---- +<1> the `units()` method is used to add the metric units to the metric. +Typically the units value is one of the constants from `org.eclipse.microprofile.metrics.MetricUnits` class. + +==== Overriding the Metric Name + +By default the metric name is the gRPC service name followed by a dot ('.') followed by the method name. +It is possible to supply a function that can be used to override the default behaviour. + +The function should implement the `io.helidon.grpc.metrics.GrpcMetrics.NamingFunction` interface +[source,java] +---- +@FunctionalInterface +public interface NamingFunction { + /** + * Create a metric name. + * + * @param service the service descriptor + * @param methodName the method name + * @param metricType the metric type + * @return the metric name + */ + String createName(ServiceDescriptor service, String methodName, MetricType metricType); +} +---- +This is a functional interface so lambda can be used too. + +For example: +[source,java] +---- +GrpcRouting routing = GrpcRouting.builder() + .intercept(GrpcMetrics.counted() + .nameFunction((svc, method, metric) -> "grpc." + service.name() + '.' + method) // <1> +---- +<1> the `NamingFunction` is just a lambda that returns the concatenated service name and method name +with the prefix `grpc.` So for a service "Foo", method "bar" the above example would produce a name "grpc.Foo.bar". + +=== Security +To enable Server Security, refer to earlier section about <> for guidance on what dependency to add in the project's pom.xml. + +==== Bootstrapping + +There are two steps to configure security with gRPC server: + +1. Create security instance and register it with server +2. Protect gRPC services of server with various security features + +[source,java] +.Example using builders +---- +// gRPC server's routing +GrpcRouting.builder() + // This is step 1 - register security instance with gRPC server processing + // security - instance of security either from config or from a builder + // securityDefaults - default enforcement for each service that has a security definition + .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate())) + // this is step 2 - protect a service + // register and protect this service with authentication (from defaults) and role "user" + .register(greetService, GrpcSecurity.rolesAllowed("user")) + .build(); +---- + +[source,java] +.Example using builders for more fine grained method level security +---- +// create the service descriptor +ServiceDescriptor greetService = ServiceDescriptor.builder(new GreetService()) + // Add an instance of gRPC security that will apply to all methods of + // the service - in this case require the "user" role + .intercept(GrpcSecurity.rolesAllowed("user")) + // Add an instance of gRPC security that will apply to the "SetGreeting" + // method of the service - in this case require the "admin" role + .intercept("SetGreeting", GrpcSecurity.rolesAllowed("admin")) + .build(); + +// Create the gRPC server's routing +GrpcRouting.builder() + // This is step 1 - register security instance with gRPC server processing + // security - instance of security either from config or from a builder + // securityDefaults - default enforcement for each service that has a security definition + .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate())) + // this is step 2 - add the service descriptor + .register(greetService) + .build(); +---- + +[source,java] +.Example using configuration +---- +GrpcRouting.builder() + // helper method to load both security and gRPC server security from configuration + .intercept(GrpcSecurity.create(config)) + // continue with gRPC server route configuration... + .register(new GreetService()) + .build(); +---- + +[source,conf] +.Example using configuration - configuration (HOCON) +---- +# This may change in the future - to align with gRPC server configuration, +# once it is supported +security + grpc-server: + # Configuration of integration with gRPC server + defaults: + authenticate: true + # Configuration security for individual services + services: + - name: "GreetService" + defaults: + roles-allowed: ["user"] + # Configuration security for individual methods of the service + methods: + - name: "SetGreeting" + roles-allowed: ["admin"] +---- + +===== Client security +When using the Helidon SE gRPC client API security can be configured for a gRPC service +or at the individual method level. The client API has a custom `CallCredentials` implementation that +integrates with the Helidon security APIs. + +[source,java] +.Example configuring client security for a service +---- +Security security = Security.builder() // <1> + .addProvider(HttpBasicAuthProvider.create(config.get("http-basic-auth"))) + .build(); + +GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <2> + .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user) + .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password) + .build(); + +ClientServiceDescriptor descriptor = ClientServiceDescriptor // <3> + .builder(StringService.class) + .unary("Lower") + .callCredentials(clientSecurity) // <4> + .build(); + +GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor); // <5> + +String response = client.blockingUnary("Lower", "ABCD"); // <6> +---- +<1> Create the Helidon `Security` instance (in this case using the basic auth provider) +<2> Create the `GrpcClientSecurity` gRPC `CallCredentials` adding the user and password +property expected by the basic auth provider. +<3> Create the gRPC `ClientServiceDescriptor` for the `StringService` gRPC service. +<4> Set the `GrpcClientSecurity` instance as the call credentials for all methods of the service +<5> Create a `GrpcServiceClient` that will allow methods to be called on the service +<6> Call the "Lower" method which will use the configured basic auth credentials + + +[source,java] +.Example configuring client security for a specific method +---- +GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <1> + .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user) + .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password) + .build(); + +ClientServiceDescriptor descriptor = ClientServiceDescriptor // <2> + .builder(StringService.class) + .unary("Lower") + .unary("Upper", rules -> rules.callCredentials(clientSecurity)) // <3> + .build(); +---- +<1> Create the `GrpcClientSecurity` call credentials in the same way as above. +<2> Create the `ClientServiceDescriptor`, this time with two unary methods, "Lower" and "Upper". +<3> The "Upper" method is configured to use the `GrpcClientSecurity` call credentials, the "Lower" method +will be called without any credentials. + + +===== Outbound security +Outbound security covers three scenarios: + +* Calling a secure gRPC service from inside a gRPC service method handler +* Calling a secure gRPC service from inside a web server method handler +* Calling a secure web endpoint from inside a gRPC service method handler + +Within each scenario credentials can be propagated if the gRPC/http method +handler is executing within a security context or credentials can be overridden +to provide a different set of credentials to use to call the outbound endpoint. + +[source,java] +.Example calling a secure gRPC service from inside a gRPC service method handler +---- +// Obtain the SecurityContext from the current gRPC call Context +SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get(); + +// Create a gRPC CallCredentials that will use the current request's +// security context to configure outbound credentials +GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(securityContext); + +// Create the gRPC stub using the CallCredentials +EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity); +---- + +[source,java] +.Example calling a secure gRPC service from inside a web server method handler +---- +private static void propagateCredentialsWebRequest(ServerRequest req, ServerResponse res) { + try { + // Create a gRPC CallCredentials that will use the current request's + // security context to configure outbound credentials + GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(req); + + // Create the gRPC stub using the CallCredentials + EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity); + + String message = req.queryParams().first("message").orElse(null); + Echo.EchoResponse echoResponse = stub.echo(Echo.EchoRequest.newBuilder().setMessage(message).build()); + res.send(echoResponse.getMessage()); + } catch (StatusRuntimeException e) { + res.status(GrpcHelper.toHttpResponseStatus(e)).send(); + } catch (Throwable thrown) { + res.status(Http.ResponseStatus.create(500, thrown.getMessage())).send(); + } +} +---- + +[source,java] +.Example calling a secure web endpoint from inside a gRPC service method handler +---- +// Obtain the SecurityContext from the gRPC call Context +SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get(); + +// Use the SecurityContext as normal to make a http request +Response webResponse = client.target(url) + .path("/test") + .request() + .property(ClientSecurity.PROPERTY_CONTEXT, securityContext) + .get(); +---- + +include::marshalling.adoc[leveloffset=2] + +== Configuration +Configure the gRPC Server using the Helidon configuration framework, either programmatically +or via a configuration file. + +=== Configuring the gRPC Server in your code + +The easiest way to configure the gRPC Server is in your application code. + +[source,java] +---- +GrpcServerConfiguration configuration = GrpcServerConfiguration.builder() + .port(8080) + .build(); +GrpcServer grpcServer = GrpcServer.create(configuration, routing); +---- + +See all configuration options +link:{javadoc-base-url}/io.helidon.grpc/GrpcServerConfiguration.html[here]. + +=== Configuring the gRPC Server in a configuration file + +You can also define the gRPC server configuration in a file. + +include::{rootdir}/config/io_helidon_grpc_server_GrpcServerConfiguration.adoc[leveloffset=1, tag=config] + +.GrpcServer configuration file example using `application.yaml` +[source,yaml] +---- +grpc: + port: 3333 +---- + +Then, in your application code, load the configuration from that file. + +[source,java] +.GrpcServer initialization using the `application.conf` file located on the classpath +---- +GrpcServerConfiguration configuration = GrpcServerConfiguration.create( + Config.builder() + .sources(classpath("application.conf")) + .build()); + +GrpcServer grpcServer = GrpcServer.create(configuration, routing); +---- + +== Examples +=== Quick Start + +Here is the code for a minimalist gRPC application that runs on a default port (1408): + +[source,java] +---- +public static void main(String[] args) throws Exception { + GrpcServer grpcServer = GrpcServer + .create(GrpcRouting.builder() + .register(new HelloService()) // <1> + .build()) + .start() // <2> + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); // Implement the simplest possible gRPC service. // <3> + + System.out.println("gRPC Server started at: http://localhost:" + grpcServer.port()); // <4> +} + +static class HelloService implements GrpcService { // <5> + @Override + public void update(ServiceDescriptor.Rules rules) { + rules.unary("SayHello", ((request, responseObserver) -> complete(responseObserver, "Hello " + request))); // <6> + rules.marshallerSupplier(new JavaMarshaller.Supplier()); // <7> + } +} +---- + +<1> Register gRPC service. +<2> Start the server. +<3> Wait for the server to start while throwing possible errors as exceptions. +<4> The server is bound to a default port (1408). +<5> Implement the simplest possible gRPC service. +<6> Add unary method `HelloService/SayHello` to the service definition. +<7> Specify custom marshaller using Java serialization to marshall requests and responses. diff --git a/docs/se/grpc/service-implementation.adoc b/docs/se/grpc/service-implementation.adoc deleted file mode 100644 index a7a7c57c00b..00000000000 --- a/docs/se/grpc/service-implementation.adoc +++ /dev/null @@ -1,165 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= gRPC Service Implementation -:description: Helidon gRPC Service Implementation -:keywords: helidon, grpc, java -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -== Service Implementation - -While Helidon gRPC Server allows you to deploy any standard gRPC service that -implements `io.grpc.BindableService` interface, including services generated -from the Protobuf IDL files (and even allows you to customize them to a certain -extent), using Helidon gRPC framework to implement your services has a number of -benefits: - -* It allows you to define both HTTP and gRPC services using similar programming - model, simplifying learning curve for developers. - -* It provides a number of helper methods that make service implementation - significantly simpler. - -* It allows you to configure some of the Helidon value-added features, such - as xref:security.adoc[security] and xref:metrics.adoc[metrics collection] - down to the method level. - -* It allows you to easily specify custom marshaller for requests and - responses if Protobuf does not satisfy your needs. - -* It provides built in support for xref:health-checks.adoc[health checks]. - -== Service Implementation Basics - -At the very basic level, all you need to do in order to implement a Helidon -gRPC service is create a class that implements `io.helidon.grpc.server.GrpcService` -interface and define one or more methods for the service: - -[source,java] ----- -class EchoService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.unary("Echo", this::echo); // <1> - } - - /** - * Echo the message back to the caller. - * - * @param request the echo request containing the message to echo - * @param observer the response observer - */ - public void echo(String request, StreamObserver observer) { // <2> - complete(observer, request); // <3> - } -} ----- - -<1> Define unary method `Echo` and map it to the `this::echo` handler. -<2> Create a handler for the `Echo` method. -<3> Send the request string back to the client by completing response observer. - -NOTE: The `complete` method shown in the example above is just one of many helper - methods available in the `GrpcService` class. See the full list - link:{grpc-server-javadoc-base-url}/io/helidon/grpc/server/GrpcService.html[here]. - -The example above implements a service with a single unary method, which will be -exposed at the `EchoService/Echo' endpoint. The service does not explicitly define -a marshaller for requests and responses, so Java serialization will be used as a -default. - -Unfortunately, this implies that you will have to implement clients by hand and -configure them to use the same marshaller as the server. Obviously, one of the -major selling points of gRPC is that it makes it easy to generate clients for a -number of languages (as long as you use Protobuf for marshalling), so let's see -how we would implement Protobuf enabled Helidon gRPC service. - -== Implementing Protobuf Services - -In order to implement Protobuf-based service, you would follow the official -link:https://grpc.io/docs/quickstart/java.html[instructions] on the gRPC -web site, which boil down to the following: - -=== Define the Service IDL - -For this example, we will re-implement the `EchoService` above as a Protobuf -service in `echo.proto` file. - -[source, proto] ----- -syntax = "proto3"; -option java_package = "org.example.services.echo"; - -service EchoService { - rpc Echo (EchoRequest) returns (EchoResponse) {} -} - -message EchoRequest { - string message = 1; -} - -message EchoResponse { - string message = 1; -} ----- - -Based on this IDL, the gRPC compiler will generate message classes (`EchoRequest` -and `EchoResponse`), client stubs that can be used to make RPC calls to the server, -as well as the base class for the server-side service implementation. - -We can ignore the last one, and implement the service using Helidon gRPC framework -instead. - -=== Implement the Service - -The service implementation will be very similar to our original implementation: - -[source,java] ----- -class EchoService implements GrpcService { - - @Override - public void update(ServiceDescriptor.Rules rules) { - rules.proto(Echo.getDescriptor()) // <1> - .unary("Echo", this::echo); // <2> - } - - /** - * Echo the message back to the caller. - * - * @param request the echo request containing the message to echo - * @param observer the response observer - */ - public void echo(Echo.EchoRequest request, StreamObserver observer) { // <3> - String message = request.getMessage(); // <4> - Echo.EchoResponse response = Echo.EchoResponse.newBuilder().setMessage(message).build(); // <5> - complete(observer, response); // <6> - } -} ----- - -<1> Specify proto descriptor in order to provide necessary type information and - enable Protobuf marshalling. -<2> Define unary method `Echo` and map it to the `this::echo` handler. -<3> Create a handler for the `Echo` method, using Protobuf message types for request and response. -<4> Extract message string from the request. -<5> Create the response containing extracted message. -<6> Send the response back to the client by completing response observer. diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index 0f7564904e1..cbea68b489d 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -76,7 +76,7 @@ Build GraphQL servers. //gRPC [CARD] .gRPC -[icon=swap_horiz,link=grpc/introduction.adoc] +[icon=swap_horiz,link=grpc/server.adoc] -- Build gRPC servers and clients. -- diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index feff72bf02c..2de65cf0d6e 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -132,24 +132,14 @@ backend: type: "icon" value: "storage" - type: "MENU" - title: "gRPC server" + title: "gRPC" dir: "grpc" glyph: type: "icon" value: "swap_horiz" sources: - - "introduction.adoc" - - "configuration.adoc" - - "routing.adoc" - - "service-implementation.adoc" - - "interceptors.adoc" - - "health-checks.adoc" - - "metrics.adoc" - - "security.adoc" - - "marshalling.adoc" - - "client-introduction.adoc" - - "client-configuration.adoc" - - "client-implementation.adoc" + - "server.adoc" + - "client.adoc" - type: "PAGE" title: "GraphQL server" source: "graphql.adoc" @@ -337,14 +327,14 @@ backend: type: "icon" value: "warning" - type: "MENU" - title: "gRPC server" + title: "gRPC" dir: "grpc" glyph: type: "icon" value: "swap_horiz" sources: - - "mp-server-side-services.adoc" - - "mp-clients.adoc" + - "server-side-services.adoc" + - "client.adoc" - type: "PAGE" title: "GraphQL" source: "graphql.adoc" diff --git a/grpc/client/pom.xml b/grpc/client/pom.xml index 7458f197507..2f7d614a224 100644 --- a/grpc/client/pom.xml +++ b/grpc/client/pom.xml @@ -100,6 +100,18 @@ provided true + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + diff --git a/grpc/client/src/main/java/io/helidon/grpc/client/GrpcChannelDescriptor.java b/grpc/client/src/main/java/io/helidon/grpc/client/GrpcChannelDescriptor.java index abdf3b7b575..25e5324e91e 100644 --- a/grpc/client/src/main/java/io/helidon/grpc/client/GrpcChannelDescriptor.java +++ b/grpc/client/src/main/java/io/helidon/grpc/client/GrpcChannelDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. + * Copyright (c) 2019, 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. @@ -18,6 +18,8 @@ import java.util.Optional; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.config.objectmapping.Value; import io.helidon.grpc.core.GrpcTlsDescriptor; @@ -127,6 +129,7 @@ public Optional tlsDescriptor() { /** * Builder builds a GrpcChannelDescriptor. */ + @Configured public static class Builder implements io.helidon.common.Builder { private boolean inProcessChannel; private String host = GrpcChannelsProvider.DEFAULT_HOST; @@ -157,6 +160,7 @@ public Builder inProcess() { * * @see io.grpc.ManagedChannelBuilder#forTarget(String) */ + @ConfiguredOption() @Value public Builder target(String target) { this.target = target; @@ -169,6 +173,7 @@ public Builder target(String target) { * * @return this instance for fluent API */ + @ConfiguredOption(value = GrpcChannelsProvider.DEFAULT_HOST) @Value(withDefault = GrpcChannelsProvider.DEFAULT_HOST) public Builder host(String host) { this.host = host; @@ -181,6 +186,7 @@ public Builder host(String host) { * * @return this instance for fluent API */ + @ConfiguredOption(value = "" + GrpcChannelsProvider.DEFAULT_PORT) @Value(withDefault = "" + GrpcChannelsProvider.DEFAULT_PORT) public Builder port(int port) { this.port = port; @@ -194,6 +200,7 @@ public Builder port(int port) { * * @return this instance for fluent API */ + @ConfiguredOption(key = "tls") @Value(key = "tls") public Builder sslDescriptor(GrpcTlsDescriptor tlsDescriptor) { this.tlsDescriptor = tlsDescriptor; diff --git a/grpc/client/src/main/java/module-info.java b/grpc/client/src/main/java/module-info.java index 6c3a1ba0629..892ea0dbaf4 100644 --- a/grpc/client/src/main/java/module-info.java +++ b/grpc/client/src/main/java/module-info.java @@ -23,4 +23,6 @@ requires transitive io.helidon.grpc.core; requires io.helidon.tracing; + + requires static io.helidon.config.metadata; } diff --git a/grpc/core/pom.xml b/grpc/core/pom.xml index a23701f1087..8c8343e6f6a 100644 --- a/grpc/core/pom.xml +++ b/grpc/core/pom.xml @@ -150,5 +150,17 @@ mockito-core test + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/GrpcTlsDescriptor.java b/grpc/core/src/main/java/io/helidon/grpc/core/GrpcTlsDescriptor.java index c6bd361b291..09cb9c12968 100644 --- a/grpc/core/src/main/java/io/helidon/grpc/core/GrpcTlsDescriptor.java +++ b/grpc/core/src/main/java/io/helidon/grpc/core/GrpcTlsDescriptor.java @@ -18,6 +18,8 @@ import io.helidon.common.configurable.Resource; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.config.objectmapping.Value; /** @@ -109,6 +111,7 @@ public Resource tlsCaCert() { /** * Builder to build a new instance of {@link GrpcTlsDescriptor}. */ + @Configured public static class Builder implements io.helidon.common.Builder { private boolean enabled = true; @@ -139,6 +142,7 @@ private Builder(Config config) { * @param enabled true to enable, false otherwise * @return this instance for fluent API */ + @ConfiguredOption(value = "true") @Value(withDefault = "true") public Builder enabled(boolean enabled) { this.enabled = enabled; @@ -150,7 +154,7 @@ public Builder enabled(boolean enabled) { * @param jdkSSL true to use JDK based SSL, false otherwise * @return this instance for fluent API */ - @Value(key = "jdk-ssl") + @ConfiguredOption(key = "jdk-ssl", value = "false") public Builder jdkSSL(boolean jdkSSL) { this.jdkSSL = jdkSSL; return this; @@ -161,6 +165,7 @@ public Builder jdkSSL(boolean jdkSSL) { * @param tlsCert the path to client's certificate * @return this instance for fluent API */ + @ConfiguredOption @Value(key = "tls-cert") public Builder tlsCert(Resource tlsCert) { this.tlsCert = tlsCert; @@ -172,6 +177,7 @@ public Builder tlsCert(Resource tlsCert) { * @param tlsKey the 's TLS private key * @return this instance for fluent API */ + @ConfiguredOption @Value(key = "tls-key") public Builder tlsKey(Resource tlsKey) { this.tlsKey = tlsKey; @@ -183,6 +189,7 @@ public Builder tlsKey(Resource tlsKey) { * @param caCert the path to CA certificate * @return this instance for fluent API */ + @ConfiguredOption(key = "tls-ca-cert") @Value(key = "tls-ca-cert") public Builder tlsCaCert(Resource caCert) { this.tlsCaCert = caCert; diff --git a/grpc/core/src/main/java/module-info.java b/grpc/core/src/main/java/module-info.java index 0ff20524262..b6aa0a60d31 100644 --- a/grpc/core/src/main/java/module-info.java +++ b/grpc/core/src/main/java/module-info.java @@ -47,6 +47,8 @@ requires jakarta.inject; + requires static io.helidon.config.metadata; + provides MarshallerSupplier with MarshallerSupplier.DefaultMarshallerSupplier, MarshallerSupplier.ProtoMarshallerSupplier, diff --git a/grpc/server/pom.xml b/grpc/server/pom.xml index 6b4563dad26..ff1baeaa4c7 100644 --- a/grpc/server/pom.xml +++ b/grpc/server/pom.xml @@ -141,6 +141,18 @@ provided true + + io.helidon.config + helidon-config-metadata + provided + true + + + io.helidon.config + helidon-config-metadata-processor + provided + true + diff --git a/grpc/server/src/main/java/io/helidon/grpc/server/GrpcServerConfiguration.java b/grpc/server/src/main/java/io/helidon/grpc/server/GrpcServerConfiguration.java index 93291ebf180..ac13a4ae89e 100644 --- a/grpc/server/src/main/java/io/helidon/grpc/server/GrpcServerConfiguration.java +++ b/grpc/server/src/main/java/io/helidon/grpc/server/GrpcServerConfiguration.java @@ -21,6 +21,8 @@ import io.helidon.common.context.Context; import io.helidon.config.Config; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.grpc.core.GrpcTlsDescriptor; import io.helidon.tracing.Tracer; @@ -146,6 +148,7 @@ static Builder builder(Config config) { /** * A {@link GrpcServerConfiguration} builder. */ + @Configured final class Builder implements io.helidon.common.Builder { private static final AtomicInteger GRPC_SERVER_COUNTER = new AtomicInteger(1); @@ -174,6 +177,10 @@ private Builder() { * @param config configuration instance * @return updated builder */ + @ConfiguredOption(key = "native", + type = Boolean.class, + value = "false", + description = "Specify if native transport should be used.") public Builder config(Config config) { if (config == null) { return this; @@ -196,6 +203,7 @@ public Builder config(Config config) { * * @return an updated builder */ + @ConfiguredOption(key = "name", value = DEFAULT_NAME) public Builder name(String name) { this.name = name == null ? null : name.trim(); return this; @@ -209,6 +217,7 @@ public Builder name(String name) { * @param port the server port * @return an updated builder */ + @ConfiguredOption(value = "" + DEFAULT_PORT) public Builder port(int port) { this.port = port < 0 ? 0 : port; return this; @@ -268,6 +277,7 @@ public Builder tracingConfig(GrpcTracingConfig tracingConfig) { * @param workers a workers count * @return an updated builder */ + @ConfiguredOption(key = "workers", value = "Number of processors available to the JVM") public Builder workersCount(int workers) { this.workers = workers; return this; diff --git a/grpc/server/src/main/java/module-info.java b/grpc/server/src/main/java/module-info.java index 8d8e6a246e4..c92ae5c618c 100644 --- a/grpc/server/src/main/java/module-info.java +++ b/grpc/server/src/main/java/module-info.java @@ -34,5 +34,7 @@ requires jakarta.annotation; requires java.logging; + requires static io.helidon.config.metadata; + requires jakarta.inject; } From c5dfcef4d4a039bf3c31a73b693c49aaca62e097 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Tue, 19 Jul 2022 16:28:27 -0700 Subject: [PATCH 45/51] Persistence documentation (#4561) Adds persistence-related documentation Signed-off-by: Laird Nelson Co-authored-by: Romain Grecourt --- docs/includes/attributes.adoc | 17 +- .../extensions/cdi-datasource-hikaricp.adoc | 84 - docs/mp/extensions/cdi-datasource-ucp.adoc | 87 - docs/mp/extensions/cdi-jta.adoc | 82 - docs/mp/extensions/overview.adoc | 21 - docs/mp/guides/datasource.adoc | 273 ---- docs/mp/guides/jpa.adoc | 565 ------- docs/mp/guides/jta.adoc | 118 -- docs/mp/guides/overview.adoc | 24 - docs/mp/introduction/introduction.adoc | 7 +- docs/mp/jpa.adoc | 53 - docs/mp/persistence.adoc | 1407 +++++++++++++++++ docs/mp/reactivemessaging/aq.adoc | 4 +- docs/sitegen.yaml | 10 +- 14 files changed, 1429 insertions(+), 1323 deletions(-) delete mode 100644 docs/mp/extensions/cdi-datasource-hikaricp.adoc delete mode 100644 docs/mp/extensions/cdi-datasource-ucp.adoc delete mode 100644 docs/mp/extensions/cdi-jta.adoc delete mode 100644 docs/mp/guides/datasource.adoc delete mode 100644 docs/mp/guides/jpa.adoc delete mode 100644 docs/mp/guides/jta.adoc delete mode 100644 docs/mp/jpa.adoc create mode 100644 docs/mp/persistence.adoc diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 90b792dabc1..331d43e12bd 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -66,8 +66,11 @@ endif::[] // 3rd party :version-lib-jaeger: 1.22 :version-lib-jedis: 2.9.0 -:version-lib-hikaricp: 2.7.8 -:version-lib-exclipselink: 3.0 +:version-lib-hikaricp: 5.0.1 +:version-lib-eclipselink: 3.0 +:version-lib-hibernate: 6.1 +:version-lib-oracle-jdbc: 21 +:version-lib-oracle-ucp: {version-lib-oracle-jdbc} :version-plugin-jib: 0.10.1 :version-plugin-jandex: 1.0.6 @@ -217,7 +220,10 @@ endif::[] :jaeger-doc-base-url: https://www.jaegertracing.io/docs/{version-lib-jaeger} :hikaricp-base-url: https://github.com/brettwooldridge/HikariCP/blob/HikariCP-{version-lib-hikaricp} :hikaricp-javadoc-url: {javadoc-io-base-url}/com.zaxxer/HikariCP/{version-lib-hikaricp} -:eclipselink-doc-base-url: https://www.eclipse.org/eclipselink/documentation/{version-lib-exclipselink} +:eclipselink-doc-base-url: https://www.eclipse.org/eclipselink/documentation/{version-lib-eclipselink} +:hibernate-doc-base-url: https://hibernate.org/orm/documentation/{version-lib-hibernate} +:hibernate-doc-jboss-url: https://docs.jboss.org/hibernate/orm/{version-lib-hibernate} +:hibernate-javadoc-base-url: {hibernate-doc-jboss-url}/javadocs :jib-base-url: https://github.com/GoogleContainerTools/jib/blob/v{version-plugin-jib}-maven/jib-maven-plugin :jedis-base-url: {javadoc-io-base-url}/redis.clients/jedis/{version-lib-jedis}/redis/clients :kafka-client-base-url: https://kafka.apache.org/28/documentation.html @@ -239,6 +245,11 @@ endif::[] :openmetrics-exemplar-spec-url: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars +:oracle-jdbc-doc-base-url: https://docs.oracle.com/en/database/oracle/oracle-database/{version-lib-oracle-jdbc}/jjdbc +:oracle-jdbc-javadoc-base-url: https://docs.oracle.com/en/database/oracle/oracle-database/{version-lib-oracle-jdbc}/jajdb +:oracle-ucp-doc-base-url: https://docs.oracle.com/en/database/oracle/oracle-database/{version-lib-oracle-ucp}/jjucp +:oracle-ucp-javadoc-base-url: https://docs.oracle.com/en/database/oracle/oracle-database/{version-lib-oracle-ucp}/jjuar + :micrometer-url: https://micrometer.io :micrometer-api-url: https://micrometer.io/docs/concepts diff --git a/docs/mp/extensions/cdi-datasource-hikaricp.adoc b/docs/mp/extensions/cdi-datasource-hikaricp.adoc deleted file mode 100644 index 7087f0af0f4..00000000000 --- a/docs/mp/extensions/cdi-datasource-hikaricp.adoc +++ /dev/null @@ -1,84 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= CDI extension for HikariCP -:description: Helidon CDI extension for HikariCP -:keywords: helidon, java, microservices, microprofile, extensions, cdi, hikaricp -:feature-name: HikariCP Support -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This link:{jakarta-cdi-spec-url}#spi[CDI portable extension] provides support for -injecting link:http://brettwooldridge.github.io/HikariCP[HikariCP data sources] in your Helidon -MicroProfile applications. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-datasource-hikaricp - ----- - -== Injecting a HikariCP data source - -The following examples show how to create a `DataSource` named `orders` in your -application. - -[source,java] -.Field-injection example ----- - @Inject - @Named("orders") - private DataSource ordersDataSource; ----- - -[source,java] -.Constructor-injection example ----- - private final DataSource ds; - @Inject - public YourConstructor(@Named("orders") DataSource ds) { - super(); - this.ds = ds; - } ----- - -The extension implements this injection point by creating a -link:{hikaricp-javadoc-url}/com/zaxxer/hikari/HikariDataSource.html[HikariDataSource] object in the -link:{jakarta-cdi-javadoc-url}/javax/enterprise/context/ApplicationScoped.html[application scope]. - -You can configure the object using -xref:../jaxrs/server-configuration.adoc[MicroProfile config]. For example, -the data source created above can be configured as follows: - -[source, properties] -.META-INF/microprofile-config.properties ----- -javax.sql.DataSource.orders.dataSourceClassName=oracle.jdbc.pool.OracleDataSource -javax.sql.DataSource.orders.dataSource.url = jdbc:oracle:thin:@localhost:1521:ORCL -javax.sql.DataSource.orders.dataSource.user = sys as sysoper -javax.sql.DataSource.orders.dataSource.password = Oracle ----- - -Property names that start with `javax.sql.DataSource.dataSourceName.` are parsed, -and the remaining portion of each name is treated as a -link:{hikaricp-base-url}/README.md#configuration-knobs-baby[Hikari connection pool property]. diff --git a/docs/mp/extensions/cdi-datasource-ucp.adoc b/docs/mp/extensions/cdi-datasource-ucp.adoc deleted file mode 100644 index ca8e24f74a5..00000000000 --- a/docs/mp/extensions/cdi-datasource-ucp.adoc +++ /dev/null @@ -1,87 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= CDI extension for Oracle UCP -:description: Helidon CDI extension for Oracle Universal Connection Pool -:keywords: helidon, java, microservices, microprofile, extensions, cdi, ucp -:feature-name: Oracle UCP Support -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This link:{jakarta-cdi-spec-url}#spi[CDI portable extension] provides support for injecting -https://docs.oracle.com/en/database/oracle/oracle-database/19/jjucp/index.html[Oracle -Universal Connection Pool data sources] in your Helidon MicroProfile -applications. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-datasource-ucp - ----- - -== Injecting an Oracle Universal Connection Pool data source - -The following examples show how to create a `DataSource` named `orders` in your -application. - -[source,java] -.Field-injection example ----- - @Inject - @Named("orders") - private DataSource ordersDataSource; ----- - -[source,java] -.Constructor-injection example ----- - private final DataSource ds; - @Inject - public YourConstructor(@Named("orders") DataSource ds) { - super(); - this.ds = ds; - } ----- - -The extension implements this injection point by creating a -https://docs.oracle.com/en/database/oracle/oracle-database/19/jjuar/oracle/ucp/jdbc/PoolDataSource.html[`PoolDataSource`] -object in the link:{jakarta-cdi-javadoc-url}/javax/enterprise/context/ApplicationScoped.html[application scope]. - -You can configure the object using xref:../jaxrs/server-configuration.adoc[MicroProfile config]. - For example, the data source created above can be configured as follows: - -[source, properties] -.META-INF/microprofile-config.properties ----- -javax.sql.DataSource.orders.connectionFactoryClassName = oracle.jdbc.pool.OracleDataSource -javax.sql.DataSource.orders.URL = jdbc:oracle:thin:@localhost:1521:ORCL -javax.sql.DataSource.orders.user = sys as sysoper -javax.sql.DataSource.orders.password = Oracle ----- - -Property names that start with `javax.sql.DataSource.dataSourceName.` -are parsed, and the remaining portion of each name is treated as a -https://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html[Java -Bean property] of the -https://docs.oracle.com/en/database/oracle/oracle-database/19/jjuar/oracle/ucp/jdbc/PoolDataSource.html[`oracle.ucp.jdbc.PoolDataSource`] -class. diff --git a/docs/mp/extensions/cdi-jta.adoc b/docs/mp/extensions/cdi-jta.adoc deleted file mode 100644 index 1ce2a0829bc..00000000000 --- a/docs/mp/extensions/cdi-jta.adoc +++ /dev/null @@ -1,82 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= CDI extension for JTA -:description: Helidon CDI extension for JTA -:keywords: helidon, java, microservices, microprofile, extensions, cdi, jta -:feature-name: JTA Support -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This link:{jakarta-cdi-spec-url}#spi[CDI portable extension] provides support for JTA (Java Transaction API) -transactions in your Helidon MicroProfile applications. - -include::{rootdir}/includes/dependencies.adoc[] - -[source,xml] ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-jta-weld - runtime - - - javax.transaction - javax.transaction-api - provided - ----- - -== Declaring a method to be transactional - -The following example shows how to declare a transactional method. - -[source,java] -.Transactional method declaration ----- -@Transactional(Transactional.TxType.REQUIRED) -public void doSomethingTransactionally() { - -} ----- - -The extension ensures that a transaction is started before and -committed after the method executes. If the method throws an -exception, the transaction will be rolled back. - -You can further specify the transactional behavior of the extension by -using different instances of the `Transactional` annotation. For more -information, see the -link:{jakarta-transactions-javadoc-url}/jakarta/transaction/Transactional.html[`Transactional` -annotation documentation]. - -Transactional method support is implemented by CDI interception -facilities. Among other things, this means that the method to which -you apply the `Transactional` annotation must not be `private` and -must in all other ways be a _business method_. See the -https://jcp.org/aboutJava/communityprocess/mrel/jsr318/index3.html[Java -Interceptors specification] for more details. - -During a transactional method invocation, the extension makes the -following objects available for injection via the `Inject` annotation: - -* link:{jakarta-transactions-javadoc-url}/jakarta/transaction/UserTransaction.html[`UserTransaction`] -* link:{jakarta-transactions-javadoc-url}/jakarta/transaction/Transaction.html[`Transaction`] -* link:{jakarta-transactions-javadoc-url}/jakarta/transaction/UserTransactionManager.html[`TransactionManager`] -* link:{jakarta-transactions-javadoc-url}/jakarta/transaction/UserTransactionSynchronizationRegistry.html[`TransactionSynchronizationRegistry`] diff --git a/docs/mp/extensions/overview.adoc b/docs/mp/extensions/overview.adoc index 11cb41588c8..5958a96118d 100644 --- a/docs/mp/extensions/overview.adoc +++ b/docs/mp/extensions/overview.adoc @@ -31,21 +31,6 @@ Helidon provides link:{jakarta-cdi-spec-url}#spi[CDI portable extensions] that y [PILLARS] ==== -[CARD] -.HikariCP data sources -[link=cdi-datasource-hikaricp.adoc] --- -Create and inject a HikariCP data source in your application code. --- - -[CARD] -.Oracle UCP data sources -[link=cdi-datasource-ucp.adoc] --- -Create and inject an Oracle Universal Connection Pool data source in -your application code. --- - [CARD] .Jedis clients [link=cdi-jedis.adoc] @@ -53,10 +38,4 @@ your application code. Create and inject a Jedis pool in your application code. -- -[CARD] -.Java Transaction API objects -[link=cdi-jta.adoc] --- -Use the Java Transaction API in your application code. --- ==== diff --git a/docs/mp/guides/datasource.adoc b/docs/mp/guides/datasource.adoc deleted file mode 100644 index c7f487a46e2..00000000000 --- a/docs/mp/guides/datasource.adoc +++ /dev/null @@ -1,273 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Helidon MP Data Source Guide -:description: Helidon MP Data Source Guide -:keywords: helidon, guide, datasource, microprofile -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This guide shows how to configure and use named link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[`DataSource`]s -in your Helidon MP application. - -== What You Need -For this 20 minute tutorial, you will need the following: - -include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites-curl] - -== Overview - -By following this guide you'll enhance a bare-bones Helidon MP -application to access an in-memory -https://www.h2database.com/html/main.html[H2 database] database. -You'll see how to install the relevant dependencies, set up and -configure the datasource, and add datasource-related code to your -application. - -== Use the Maven Archetype to Generate a Helidon MP Application - -In a shell, `cd` into an empty directory and run this: - -[source,bash,subs="attributes+"] ----- -mvn -U archetype:generate \ - -DinteractiveMode=false \ - -DarchetypeGroupId=io.helidon.archetypes \ - -DarchetypeArtifactId=helidon-bare-mp \ - -DarchetypeVersion={helidon-version} \ - -DgroupId=io.helidon.example \ - -DartifactId=helidon-ds \ - -Dpackage=io.helidon.example.ds \ - -DrestResourceName=ExampleResource \ - -DapplicationName=ExampleApplication ----- - -Now `cd` into `helidon-ds`. The rest of this guide will assume all -relative paths are relative to this directory. - -=== Add the H2 Database Driver to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - com.h2database - h2 - 1.4.199 - runtime - ----- - -In a production application, you may use a different database, so in -that case you may add a different database driver dependency here -instead. - -=== Add the Hikari Connection Pool Extension to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-datasource-hikaricp - runtime - ----- - -=== Add an `application.yaml` File With Database Connectivity Information - -Replace the contents of the following file under `src/main/resources`: - -[source,yaml] -.`src/main/resources/application.yaml` ----- -server: - port: 8080 -javax: - sql: - DataSource: <1> - test: <2> - dataSourceClassName: org.h2.jdbcx.JdbcDataSource <3> - dataSource: <4> - url: jdbc:h2:mem:test <5> - user: sa - password: "" - ----- - -<1> This `javax:`/`sql:`/`DataSource:` preamble is required. - -<2> `test` is the name of the `DataSource` being configured here. - -<3> `dataSourceClassName` is an -{hikaricp-base-url}/README.md#configuration-knobs-baby[essential -Hikari connection pool property]. - -<4> `dataSource` is a -{hikaricp-base-url}/README.md#initialization[Hikari -connection pool keyword]. - -<5> These are some of the Java Beans-compliant properties exposed by, -in this case, the -https://www.h2database.com/javadoc/org/h2/jdbcx/JdbcDataSource.html[`org.h2.jdbcx.JdbcDataSource` -class]. - -=== Inject a Datasource into Your Application Code - -NOTE: For more information about `DataSource`, see the link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[javadoc]. - -In the `src/main/java/io/helidon/example/ds/ExampleResource.java` file, add the following imports: - -[source,java] -.`src/main/java/io/helidon/example/ds/ExampleResource.java` ----- -import jakarta.enterprise.context.Dependent; -import jakarta.inject.Inject; -import jakarta.inject.Named; -import javax.sql.DataSource; ----- - -Annotate the resource class declaration with `@Dependent`: - -[source,java] -.`src/main/java/io/helidon/example/ds/ExampleResource.java` ----- -@Dependent <1> -public class ExampleResource { ----- - -<1> This ensures that `io.helidon.example.jpa.ExampleResource` is a -discoverable CDI bean. - -Then add the following annotated field declaration: - -[source,java] -.`src/main/java/io/helidon/example/ds/ExampleResource.java` ----- -@Inject <1> -@Named("test") <2> -private DataSource testDataSource; ----- - -<1> The -http://javax-inject.github.io/javax-inject/api/javax/inject/Inject.html[`@Inject` -annotation] is used to indicate that the CDI container should set the -annotated field automatically. - -<2> The -http://javax-inject.github.io/javax-inject/api/javax/inject/Named.html[`@Named` -annotation] is used to select which data source should be injected. -Here, the `test` data source is requested. - -=== Use The Injected DataSource - -Now that you have a `DataSource`, you'll use it to connect to the database. - -First, ensure the `io.helidon.example.ds.ExampleResource` resource -class imports various `java.sql` classes: - -[source,java] -.`src/main/java/io/helidon/example/ds/ExampleResource.java` ----- -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; ----- - -Add the following resource method to the `ExampleResource` class: - -[source,java] -.`src/main/java/io/helidon/example/ds/ExampleResource.java` ----- -@GET -@Path("tables") -@Produces("text/plain") -public String getTableNames() throws SQLException { <1> - StringBuilder sb = new StringBuilder(); - try (Connection connection = this.testDataSource.getConnection(); <2> - PreparedStatement ps = - connection.prepareStatement(" SELECT TABLE_NAME" <3> - + " FROM INFORMATION_SCHEMA.TABLES " - + "ORDER BY TABLE_NAME ASC"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - sb.append(rs.getString(1)).append("\n"); - } - } - return sb.toString(); -} ----- - -<1> Database interactions can throw `SQLException`. - -<2> We acquire a `Connection`, a `PreparedStatement` and a `ResultSet` -in a try-with-resources block. - -<3> This SQL statement returns a list of all table names in the database. - -== Build the Application - -Execute the following from the root directory of your application: - -[source,bash] ----- -mvn clean package ----- - -== Run the Application - -Execute the following from the root directory of your application: - -[source,bash] ----- -java -jar target/helidon-ds.jar ----- - -== Test the Application - -Execute the following: - -[source,bash] ----- -curl http://localhost:8080/example/tables ----- - -Observe that the result will be a list of database table names. - -== Related Examples - -Helidon features a few examples of projects that use datasources. - -* link:{helidon-github-tree-url}/examples/integrations/cdi/datasource-hikaricp-h2[An - example showing a Hikari connection pool data source connected to an - H2 database] - -* link:{helidon-github-tree-url}/examples/integrations/cdi/datasource-hikaricp-mysql[An - example showing a Hikari connection pool data source connected to a - MySQL database] - -Some examples' configurations can be found in their -`META-INF/microprofile-config.properties` resources instead of in an -`application.yaml` file as described above. Though the syntax is -different, the same principles as those described above still apply. diff --git a/docs/mp/guides/jpa.adoc b/docs/mp/guides/jpa.adoc deleted file mode 100644 index d44c3664fbf..00000000000 --- a/docs/mp/guides/jpa.adoc +++ /dev/null @@ -1,565 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Helidon MP JPA Guide -:description: Helidon MP JPA Guide -:keywords: helidon, guide, transaction, jpa, microprofile -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This guide shows how to configure and use the link:{jakarta-persistence-spec-url}[Java Persistence API (JPA)] from - within a Helidon MP application. - -== What You Need -For this 30 minute tutorial, you'll need the following: - -include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites-curl] - -== Overview - -By following this guide, you’ll enhance a bare-bones Helidon MP -application to use JPA, with automatic transaction support, backed by -link:https://www.eclipse.org/eclipselink/#jpa[EclipseLink], to access an -in-memory https://www.h2database.com/html/main.html[H2 -database]. You’ll see how to install the relevant dependencies and add -JPA-related code to your application. - -NOTE: This guide assumes that you have read the following: - xref:datasource.adoc[An understanding of named data source support in Helidon MP] and - xref:jta.adoc[An understanding of transaction support in Helidon MP]. - -== Use the Maven Archetype to Generate a Helidon MP Application - -In a shell, `cd` into an empty directory and run this: - -[source,bash,subs="attributes+"] ----- -mvn -U archetype:generate \ - -DinteractiveMode=false \ - -DarchetypeGroupId=io.helidon.archetypes \ - -DarchetypeArtifactId=helidon-bare-mp \ - -DarchetypeVersion={helidon-version} \ - -DgroupId=io.helidon.example \ - -DartifactId=helidon-jpa \ - -Dpackage=io.helidon.example.jpa ----- - -Now `cd` into `helidon-jpa`. The rest of this guide will assume all -relative paths are relative to this directory. - -=== Add the H2 Database Driver to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - com.h2database - h2 - runtime - ----- - -In a production application, you may use a different database, so in -that case you may add a different database driver dependency here -instead. - -=== Add the Hikari Connection Pool Extension to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-datasource-hikaricp - runtime - ----- - -=== Add the JTA Extension to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-jta-weld - runtime - ----- - -=== Add the Provider-Independent Helidon JPA Extension to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-jpa - runtime - ----- - -=== Add the EclipseLink JPA Extension to the Runtime Classpath - -Add the following dependency in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-eclipselink - runtime - ----- - -=== Add the JTA and JPA Dependencies to the Provided Classpath - -Add the following dependencies in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - - jakarta.ws.rs-api - compile - - - jakarta.persistence - jakarta.persistence-api - compile - - - jakarta.transaction - jakarta.transaction-api - compile - - ----- - -=== Add DDL to Create the Relevant Database Tables - -Add the following file under `src/main/resources`: - -[source,sql] -.`src/main/resources/greeting.ddl` ----- -CREATE TABLE IF NOT EXISTS GREETING ( - SALUTATION VARCHAR(64) NOT NULL PRIMARY KEY, - RESPONSE VARCHAR(64) NOT NULL -); - -MERGE INTO GREETING (SALUTATION, RESPONSE) VALUES ('Marco', 'Polo'); ----- - -=== Add an `application.yaml` File With Database Connectivity Information - -Replace the contents of the following file under `src/main/resources`: - -[source,yaml] -.`src/main/resources/application.yaml` ----- -server: - port: 8080 -javax: - sql: - DataSource: - greetingDataSource: - dataSourceClassName: org.h2.jdbcx.JdbcDataSource - dataSource: - url: jdbc:h2:mem:greeting;INIT=RUNSCRIPT FROM 'classpath:greeting.ddl' <1> - user: sa - password: "" - ----- - -<1> The -http://www.h2database.com/html/features.html#execute_sql_on_connection[H2 -`INIT` property] tells H2 what command to run upon starting up. In -this case, it is going to -http://www.h2database.com/html/commands.html#runscript[load and run] -the DDL mentioned above. - -=== Add a Java Class to Represent a Greeting JPA Entity - -Add the following Java class under `src/main/java/io/helidon/example/jpa`: - -[source,java] -.`src/main/java/io/helidon/example/jpa/Greeting.java` ----- -package io.helidon.example.jpa; - -import java.io.Serializable; -import java.util.Objects; - -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Basic; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -@Access(value = AccessType.FIELD) // <1> -@Entity(name = "Greeting") // <2> -@Table(name = "GREETING") // <3> -public class Greeting implements Serializable { // <4> - - @Column( - insertable = true, - name = "SALUTATION", // <5> - nullable = false, - updatable = false - ) - @Id // <6> - private String salutation; - - @Basic(optional = false) // <7> - @Column( - insertable = true, - name = "RESPONSE", - nullable = false, - updatable = true - ) - private String response; - - @Deprecated - protected Greeting() { // <8> - super(); - } - - public Greeting(String salutation, String response) { // <9> - super(); - this.salutation = Objects.requireNonNull(salutation); - this.setResponse(response); - } - - public String getSalutation() { - return this.salutation; - } - - public String getResponse() { - return this.response; - } - - public void setResponse(String response) { - this.response = Objects.requireNonNull(response); - } - - @Override - public String toString() { - return this.getSalutation() + " " + this.getResponse(); - } - -} ----- - -<1> (Some annotations in this example, like this one, have sensible defaults, but the example specifies them explicitly - for clarity.) This link:{jakarta-persistence-javadoc-url}/jakarta/persistence/access[`Access` annotation] says that - JPA will access this class' fields directly, rather than via getter and setter methods. - -<2> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/entity[`Entity` annotation] identifies this class as - a JPA entity. The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/entity#name()[`name` element] value can be - used in JPQL queries. - -<3> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/table[`Table` annotation] identifies the database - table to which this class will be mapped. - -<4> JPA entities should be link:{jdk-javadoc-url}/java.base/java/io/Serializable.html[`Serializable`]. - -<5> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/column[`Column` annotation] - specifies what column in the database the annotated field maps to. The elements of the `Column` annotation further - describe the column. - -<6> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/id[`Id` annotation] indicates this field will be - mapped to the primary key of the database table. - -<7> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/basic[`Basic annotation] indicates this field will - be mapped to an ordinary ("basic") column. - -<8> All JPA entities need a zero-argument constructor, but it doesn't have to be `public`. This constructor satisfies - this requirement. It is marked link:{jdk-javadoc-url}/java.base/java/lang/Deprecated.html[`Deprecated`] and is - non-`public` so that normal users have to supply data for the `salutation` and `response` fields via the other - constructor. - -<9> This is the constructor normal users will use. - -=== Add a `META-INF/persistence.xml` Descriptor - -Add the following file under `src/main/resources/META-INF`: - -[source,xml] -.`src/main/resources/META-INF/persistence.xml` ----- - - - - A persistence unit for the greeting example. - greetingDataSource - io.helidon.example.jpa.Greeting - - - - - - - - - - - ----- - -<1> Helidon MP's JPA extension supports JPA 3.0. - -<2> Note that `JTA` is the transaction type. JTA transactions are -fully supported. - -<3> Note that the name of the data source is the one configured in the -`application.yaml` file described earlier. - -<4> The `Greeting` class you created is listed here. - -<5> The properties listed here are in general -{eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm[EclipseLink -properties]. Many are optional, but a few (detailed below) are required. - -<6> {eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm#target-database[This -property] is required when EclipseLink is the JPA provider. It is set -to `org.eclipse.persistence.platform.database.H2Platform` because this -example uses the H2 database. - -<7> {eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm#target-server[This -property] is required, and when EclipseLink is the JPA provider must -have the value -`io.helidon.integrations.cdi.eclipselink.CDISEPlatform`. - -<8> {eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm#weaving[This -property] is required when EclipseLink is the JPA provider and must be -set to `false`. - -=== Modify the `pom.xml` File To Support Static Weaving - -_Weaving_ is the term that describes the bytecode manipulation that -JPA providers perform upon your simple Java entity classes (like the -`Greeting` class you created above). In Helidon MicroProfile's JPA -extension, weaving must be performed statically (at build time). Here -we modify the `pom.xml` to make that happen. - -Add the following plugin configuration in your `pom.xml`: - -:static-weaving-url: https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Command_Line - -[source,xml] -.`pom.xml` ----- - - org.codehaus.mojo - exec-maven-plugin - - - weave - process-classes - - java - - - org.eclipse.persistence.tools.weaving.jpa.StaticWeave - - -loglevel - INFO - ${project.build.outputDirectory} - ${project.build.outputDirectory} - - - - - ----- -<1> The link:{static-weaving-url}[Eclipselink-supplied `StaticWeave` class] is used to weave classes. -<2> Weaving is performed on compiled classes. -<3> Weaving is performed "in place". - -== Modify the `pom.xml` File To Support Generating the Static Metamodel - -The -https://jakarta.ee/specifications/persistence/3.0/apidocs/jakarta.persistence/jakarta/persistence/metamodel/package-summary.html[_static -metamodel_] is a model of your entity classes. It is typically generated at compile time. - -Add the following plugin configuration in your `pom.xml`: - -[source,xml] -.`pom.xml` ----- - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.hibernate.orm - hibernate-jpamodelgen - ${version.lib.hibernate} - - - - ----- - -<1> Hibernate supplies a vendor-independent annotation processor that - generates any JPA project's static metamodel. It is added here so - that the compiler is aware of it. Without further configuration, - you can find the generated static metamodel source code under - `target/generated-sources/annotations`. - -== Inject a Container-Managed `EntityManager` - -In the `src/main/java/io/helidon/example/jpa/GreetResource.java` file, add the following imports: - -[source,java] -.`src/main/java/io/helidon/example/jpa/GreetResource.java` ----- -import jakarta.enterprise.context.Dependent; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; ----- - -Annotate the resource class declaration with `@Dependent`: - -[source,java] -.`src/main/java/io/helidon/example/jpa/GreetResource.java` ----- -@Dependent // <1> -public class GreetResource { - //... -} ----- - -<1> This ensures that `io.helidon.example.jpa.GreetResource` is a -discoverable CDI bean, because it is an example of a -https://jakarta.ee/specifications/cdi/2.0/cdi-spec-2.0.html#bean_defining_annotations[bean-defining -annotation]. - -Then add the following annotated field declaration: - -[source,java] -.`src/main/java/io/helidon/example/jpa/GreetResource.java` ----- -@PersistenceContext // <1> -private EntityManager em; ----- -<1> The link:{jakarta-persistence-javadoc-url}/jakarta/persistence/persistencecontext[`@PersistenceContext` -annotation] indicates that you want an -link:{jakarta-persistence-javadoc-url}/jakarta/persistence/entitymanager[`EntityManager`] -injected here. - -== Use the Injected `EntityManager` - -In the `src/main/java/io/helidon/example/jpa/GreetResource.java` -file, add the following import: - -[source,java] -.`src/main/java/io/helidon/example/jpa/GreetResource.java` ----- -import jakarta.transaction.Transactional; -import jakarta.ws.rs.PathParam; ----- - -Add the following resource method to the `GreetResource` class: - -[source,java] -.`src/main/java/io/helidon/example/jpa/GreetResource.java` ----- -@GET -@Path("response/{salutation}") -@Produces("text/plain") -@Transactional // <1> -public String getResponse(@PathParam("salutation") String salutation) { - final Greeting greeting = this.em.find(Greeting.class, salutation); - final String returnValue; - if (greeting == null) { - returnValue = null; - } else { - returnValue = greeting.getResponse(); - } - return returnValue; -} ----- -<1> A JTA transaction will be automatically started at the beginning -of this method when it is invoked as a result of an incoming HTTP -request, and committed or rolled back when the method terminates -normally or exceptionally. The injected `EntityManager` will join the -transaction automatically. - -== Add Logging - -Add the following content to the `logging.properties` file under -`src/main/resources`: - -[source,properties] -.`src/main/resources/logging.properties` ----- -com.zaxxer.hikari.level=INFO -h2database.level=WARNING -io.netty.level=INFO -org.eclipse.persistence.level=FINE -org.glassfish.jersey.server.level=CONFIG ----- - -== Build the Application - -Execute the following from the root directory of your application: - -[source,bash] ----- -mvn package ----- - -== Run the Application - -Execute the following from the root directory of your application: - -[source,bash] ----- -java -jar target/helidon-jpa.jar ----- - -== Test the Application - -Execute the following: - -[source,bash] ----- -curl http://localhost:8080/greet/response/Marco ----- - -Observe that `Polo` is returned. diff --git a/docs/mp/guides/jta.adoc b/docs/mp/guides/jta.adoc deleted file mode 100644 index dd608ca3714..00000000000 --- a/docs/mp/guides/jta.adoc +++ /dev/null @@ -1,118 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Helidon MP JTA Guide -:description: Helidon MP JTA Guide -:keywords: helidon, guide, transaction, jta, microprofile -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -This guide shows how to configure and use -https://jakarta.ee/specifications/transactions/1.3/[Java Transaction API -(JTA)]-compliant transactions in your Helidon MP application. - -== What You Need - -For this 10 minute tutorial, you will need the following: - -include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] - -== Add the Helidon JTA Integration to Your Application's Runtime Classpath - -To bring JTA transactions to your Helidon MP application, you'll need -to add the relevant extension. Specifically, you'll need to add an -appropriate `` element as a child element of the -`` element in your `pom.xml`, referencing the Helidon -JTA extension: - -[source,xml] ----- - - io.helidon.integrations.cdi - helidon-integrations-cdi-jta-weld - runtime - ----- - -<1> Note the scope is `runtime`. - -== Add JTA to Your Application's Compilation Classpath - -To actually use the Java Transaction API in your code, you'll need to -ensure a library defining the classes and interfaces mandated by the -specification is present on your compilation classpath. (Note that -this library is separate from any given vendor's actual implementation -of the specification by way of these classes and interfaces.) - -[source,xml] ----- - - jakarta.transaction - jakarta.transaction-api - provided - ----- - -<1> The scope is `provided` to allow the JTA implementation runtime to -provide its own implementation of the API jar if necessary. - -== Annotate a Method With https://jakarta.ee/specifications/transactions/1.3/apidocs/javax/transaction/transactional[`@Transactional`] - -Choose a method that you wish to have a certain kind of transactional -behavior, and annotate it with the -https://jakarta.ee/specifications/transactions/1.3/apidocs/javax/transaction/transactional[`@Transactional`] -annotation. - -The method in question will need to be a business method of some kind: -a method that is invoked by the Helidon MP server machinery, not -directly by the user. This is because normally the behavior that -`@Transactional` requests is provided by interceptor functionality. -More concretely, in Helidon MP you can annotate a -https://javaee.github.io/tutorial/jaxrs002.html#GILQB[JAX-RS resource -method], or a method on a CDI bean that itself is injected in your -application somewhere. - -For example, a method on a hypothetical `PersonDAO` class that saves a -hypothetical `Person` object to a database, starting a new JTA -transaction if necessary, might look like this: - -[source,java] -.`PersonDAO.java` ----- -import jakarta.transaction.Transactional; -import jakarta.transaction.Transactional.TxType; - -@Transactional(TxType.REQUIRED) <1> -public void savePerson(Person person) { - // Use JPA or another JTA-aware framework to save the Person object <2> -} ----- - -<1> The `Transactional` annotation indicates the kind of transactional -behavior you would like this method to have. In this example, we -explicitly set the kind of behavior to be -https://jakarta.ee/specifications/transactions/1.3/apidocs/javax/transaction/transactional.txtype#REQUIRED[`REQUIRED`] -(which also happens to be the default if you do not specify an -explicit -https://jakarta.ee/specifications/transactions/1.3/apidocs/javax/transaction/transactional.txtype[`TxType`]). - -<2> Annotating a method with `@Transactional` demarcates a JTA -transaction, but it is up to individual JTA-aware frameworks and -libraries to actually do something when the transaction is implicitly -started. JPA is an example of a framework that is JTA aware. diff --git a/docs/mp/guides/overview.adoc b/docs/mp/guides/overview.adoc index 54f9d0b25ef..552b34b764e 100644 --- a/docs/mp/guides/overview.adoc +++ b/docs/mp/guides/overview.adoc @@ -67,30 +67,6 @@ Learn how to use Helidon MP built-in and application metrics. Learn how to trace a Helidon MP application. -- -[CARD] -.Using DataSources -[link=datasource.adoc] --- -Learn how to configure and use DataSources -in your Helidon MP application. --- - -[CARD] -.Using Transactions -[link=jta.adoc] --- -Learn how to configure and use Java Transaction API (JTA)-compliant transactions -in your Helidon MP application. --- - -[CARD] -.Using JPA -[link=jpa.adoc] --- -Learn how to configure and use the Java Persistence API (JPA) -in your Helidon MP application. --- - [CARD] .Helidon MP Tutorial [link=mp-tutorial.adoc] diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index 90f3ff4f907..527356e371c 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -123,10 +123,11 @@ Helidon MP supports building RESTful services using JAX-RS/Jersey. //JPA [CARD] -.JPA -[icon=dns,link=../jpa.adoc] +.Persistence +[icon=dns,link=../persistence.adoc] -- -Work with JPA in Helidon MP in all the ways that you’re familiar with. +Work with named data sources, JTA and JPA in Helidon MP in all the +ways that you’re familiar with. -- diff --git a/docs/mp/jpa.adoc b/docs/mp/jpa.adoc deleted file mode 100644 index e1e700cadac..00000000000 --- a/docs/mp/jpa.adoc +++ /dev/null @@ -1,53 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2020, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Helidon MP JPA -:toc: -:toc-placement: preamble -:description: Jakarta Persistence support in Helidon MP -:keywords: helidon, mp, microprofile, persistence, database -:rootdir: {docdir}/.. - -include::{rootdir}/includes/mp.adoc[] - -== Overview -Helidon MP supports JPA in much the same way that Java EE application -servers do, but with much less weight. If you come from a Java EE -background, you'll feel right at home: you work with JPA in Helidon MP -in all the ways that you're familiar with. - -For example, in Helidon MP's JPA integration, you can work with a -fully managed `EntityManager` by injecting it in the same way you -would in a Java EE application server: - -[source,java] ----- -@PersistenceContext -private EntityManager em; ----- - -The Jakarta Persistence API is a specification that governs how Java -objects map to relational databases, and has existed since 2006. -Hibernate and Eclipselink, two of the most popular JPA -implementations, are supported by Helidon MP JPA. - -== Next Steps -Learn more about the link:{jakarta-persistence-spec-url}[Java Persistence API (JPA)] - -Configure and use the Java Persistence API (JPA) from -within a Helidon MP application. xref:guides/jpa.adoc[Helidon MP JPA Guide]. diff --git a/docs/mp/persistence.adoc b/docs/mp/persistence.adoc new file mode 100644 index 00000000000..39d0cdb8de3 --- /dev/null +++ b/docs/mp/persistence.adoc @@ -0,0 +1,1407 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += Persistence +:description: Persistence-related support in Helidon MP +:keywords: datasource, helidon, jpa, jta, microprofile, mp +:rootdir: {docdir}/.. + +include::{rootdir}/includes/mp.adoc[] + +== Contents + +* <> +* <> +** <> +*** <> +**** <> +***** <> +**** <> +***** <> +*** <> +**** <> +***** <> +**** <> +***** <> +** <> +*** <> +** <> +* <> +** <> +** <> +** <> +* <> +** <> +*** <> +*** <> +*** <> +*** <> +*** <> +*** <> +** <> +** <> +** <> +* <> + +== Overview [[Overview]] + +Helidon MP comes with deep integration for three +specification-defined, broadly persistence-related technologies that +can be used together or separately: + +* <> +* <> +* <> + +Each integration's setup, configuration, and usage are described +below. + +== Named Data Source Integration [[DS]] + +=== Overview + +Helidon MP's named data source integration allows you to safely inject +managed +link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[`javax.sql.DataSource`] +instances that are annotated with +link:{jakarta-inject-javadoc-url}/jakarta/inject/named[`jakarta.inject.Named` +annotations] into your Helidon MP application. +link:{jdk-javadoc-url}/java.sql/java/sql/Connection.html[`java.sql.Connection` +objects] +link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html#getConnection()[acquired] +from these data sources will be pooled by your choice of one of two +possible connection pool implementations. + +The connections managed by the connection pool will be supplied by +your relational database vendor's JDBC driver. + +How you set up Helidon MP's named data source integration differs +depending on which of these two connection pools, which JDBC driver, +and which relational database product you use. + +Representative setups are described below. The list of such setups is +not exhaustive. + +=== Project Setup [[DS-Setup]] + +==== Setting Up a Connection Pool [[DS-CP-Project-Setup]] + +===== Overview + +Helidon MP's named data source integration requires a connection pool +implementation. + +Helidon MP comes with support for two connection +pools: + +1. https://github.com/brettwooldridge/HikariCP[HikariCP] + +2. link:{oracle-ucp-doc-base-url}index.html[Oracle Universal + Connection Pool] + +You can choose to use either, but not both. + +Details concerning each connection pool's setup are described below. + +===== Setting Up the HikariCP Connection Pool [[DS-HikariCP-Project-Setup]] + +====== Maven Coordinates (HikariCP) [[DS-HikariCP-Maven-Coordinates]] + +To include the https://github.com/brettwooldridge/HikariCP[HikariCP +connection pool] in your Helidon MP application: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* Ensure the following `` element is present as a child + element of your project's `pom.xml` file's `` element: ++ +[source,xml] +---- + + io.helidon.integrations.cdi + helidon-integrations-cdi-datasource-hikaricp + runtime + +---- +<1> The `scope` is `runtime`, indicating that the HikariCP integration + will be available on the runtime classpath. + +===== Setting up the Oracle Universal Connection Pool [[DS-UCP-Project-Setup]] + +====== Maven Coordinates (Oracle Universal Connection Pool) [[DS-UCP-Maven-Coordinates]] + +To include the link:{oracle-ucp-doc-base-url}index.html[Oracle +Universal Connection Pool] in your Helidon MP application: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* Ensure the following `` element is present as a child + element of your project's `pom.xml` file's `` element: ++ +[source,xml] +---- + + io.helidon.integrations.cdi + helidon-integrations-cdi-datasource-ucp + runtime + +---- +<1> The `scope` is `runtime`, indicating that the Oracle + Universal Connection Pool integration will be available on the + runtime classpath. + +==== Setting Up a Database Driver [[DS-Driver-Project-Setup]] + +===== Overview + +Regardless of which connection pool you use, at the lowest level JDBC +database driver classes are responsible for making any connections to +a relational database. JDBC database driver classes are +database-product-specific. + +Once you have decided upon a relational database product to use, and +JDBC driver classes to use to connect to it, +xref:../about/managing-dependencies.adoc[ensure your dependencies are +managed], and then ensure that a `runtime`-scoped `` +element describing your JDBC driver classes is present as a child +element of your project's `pom.xml` file's `` element. + +See the +link:{jdk-javadoc-url}/java.sql/java/sql/package-summary.html[JDBC 4.3 +Specification] for more information about JDBC. + +Representative setups are described below. The list of such setups is +not exhaustive. + +===== Setting Up H2 [[DS-H2-Driver-Project-Setup]] + +====== Maven Coordinates (H2) [[DS-H2-Driver-Maven-Coordinates]] + +To include the https://www.h2database.com/html/main.html[H2 JDBC +driver] classes in your Helidon MP application so your application can +https://www.h2database.com/html/features.html#database_url[connect to +an H2 database] (whether in-memory or persistent): + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* Ensure the following `` element is present as a child + element of your project's `pom.xml` file's `` element: ++ +[source,xml] +---- + + com.h2database + h2 + runtime + +---- +<1> The `scope` is `runtime`, indicating that the H2 + JDBC driver classes will be available on the runtime classpath. + +===== Setting Up Oracle JDBC [[DS-Oracle-Driver-Project-Setup]] + +====== Maven Coordinates (Oracle JDBC) [[DS-Oracle-Driver-Maven-Coordinates]] + +To include the link:{oracle-jdbc-javadoc-base-url}/index.html[Oracle +JDBC driver classes] in your Helidon MP application so your +application can +link:{oracle-jdbc-doc-base-url}/data-sources-and-URLs.html#GUID-EF07727C-50AB-4DCE-8EDC-57F0927FF61A[connect +to an Oracle database]: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* Read and understand + https://www.oracle.com/database/technologies/maven-central-guide.html[Developer's + Guide For Oracle JDBC 21c on Maven Central] + +* For a basic setup, ensure the following `` element is + present as a child element of your project's `pom.xml` file's + `` element: ++ +[source,xml] +---- + + com.oracle.database.jdbc + ojdbc11 + 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 17, which is the Java version targeted by Helidon 3. +<3> The `scope` is `runtime`, indicating that the Oracle + JDBC driver classes will be available on the runtime classpath. + +=== Configuration [[DS-Configuration]] + +==== Overview + +Each connection pool supported by Helidon's named data source +integration support is, itself, a `DataSource` that wraps a +_vendor-supplied_ `DataSource` present in the JDBC driver classes you +added to your project. You must configure both the pool and the +vendor-supplied `DataSource`. + +To configure Helidon MP's named data source integration: + +1. Decide where each property of the configuration will reside, as + permitted by xref:config/introduction.adoc[Helidon MP's + MicroProfile Config implementation] + +2. Create configuration suitable for the combination of your selected + connection pool and your selected vendor-supplied `DataSource` + implementation in those locations + +Helidon MP's named data source integration relies on +xref:config/introduction.adoc[Helidon MP's usage of MicroProfile +Config], so you have many choices for each configuration property when +deciding on your configuration's location in (1) above. + +The configuration property values themselves are necessarily specific +to the connection pool you selected, and to the vendor-supplied +`DataSource` responsible for actually connecting to your relational +database. In general, at a minimum, in your configuration you +typically supply: + +* Information so the connection pool knows which vendor-supplied + `DataSource` implementation to manage + +* A JDBC URL specific to the vendor-supplied `DataSource` describing + where the database is located, so the managed vendor-supplied + `DataSource` knows how to connect to it + +* Information required for the vendor-supplied `DataSource` to + authenticate to the database and otherwise tailor itself to it + +Some examples for representative configurations follow. The list of +such configurations is not exhaustive. + +==== Configuration Prefixes [[DS-Configuration-Prefixes]] + +All MicroProfile Config-compatible property names for Helidon MP's +named data source integration follow a common pattern: + +[example] +*_objecttype_*.*_datasourcename_*.*_propertyname_* + +* The name of a given configuration property always begins with the + *_objecttype_* portion: a fully-qualified Java class name of the + object being configured. Configuration for Helidon MP's named + data source integration concerns the behavior of + `javax.sql.DataSource` objects, so Helidon MP's named data source + integration configuration property names begin with + `javax.sql.DataSource`. + +** A period (`.`) separates the _objecttype_ portion from the rest of + the property name. + +* The *_datasourcename_* portion, the name of the data source being + configured, comes next. It cannot contain a period (`.`). + +** A period (`.`) separates the _datasourcename_ portion from the rest + of the property name. + +* The *_propertyname_* portion, identifying the connection-pool- or + vendor-supplied-`DataSource`-specific configuration property name, + comes last. It may contain periods (`.`). + +As an example, configuration to set an imaginary `foo.bar` property on +the `test` data source's associated connection pool or vendor-specific +`DataSource` to `baz` looks like this in Java `.properties` format: + +[source,properties] +---- +javax.sql.DataSource.test.foo.bar=baz # <1> <2> <3> +---- +<1> The *_objecttype_* portion of the configuration property name is + `javax.sql.DataSource`. +<2> The *_datasourcename_* portion of the configuration property name + is `test`. +<3> The *_propertyname_* portion of the configuration property name is + `foo.bar`. + +==== Examples [[DS-Configuration-Examples]] + +Here are some examples illustrating general named data source +configuration patterns in various +xref:config/advanced-configuration.adoc[common MicroProfile +Config-compatible locations]. + +===== Example: `META-INF/microprofile-config.properties` Classpath Resource + +Here is an example of some named data source configuration as might be +found in a +`src/main/resources/META-INF/microprofile-config.properties` +configuration source: + +[source,properties] +---- +javax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource = itsValue +javax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource = anotherValue +---- + +===== Example: System Properties Set on the Command Line + +Here is an example of some named data source configuration using +system properties on the command line instead: + +[source,bash] +---- +java \ + -Djavax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource=itsValue \ + -Djavax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource=anotherValue \ + # ... +---- + +===== Example: Environment Variables Set on the Command Line + +Here is an example of some named data source configuration using +environment variables as typed directly into a command line shell, +relying on +link:{microprofile-config-javadoc-url}/org/eclipse/microprofile/config/spi/ConfigSource.html#default_config_sources[MicroProfile +Config's mapping rules], since many shells will not understand +environment variable names with periods (.) in them: + +[source,bash] +---- +JAVAX_SQL_DATASOURCE_YOURDATASOURCENAME_SOMEPROPERTYOFYOURCONNECTIONPOOLANDDATASOURCE=itsValue \ +JAVAX_SQL_DATASOURCE_YOURDATASOURCENAME_SOMEOTHERPROPERTYOFYOURCONNECTIONPOOLANDDATASOURCE=anotherValue \ +java # ... +---- + +===== Example: Environment Variables Set By the `env` Command + +Here is an example of some named data source configuration using +environment variables as supplied via the +https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html[`env` +shell command], thus removing the need for +link:{microprofile-config-javadoc-url}/org/eclipse/microprofile/config/spi/ConfigSource.html#default_config_sources[MicroProfile +Config's mapping rules]: + +[source,bash] +---- +env 'javax.sql.DataSource.yourDataSourceName.somePropertyOfYourConnectionPoolAndDataSource=itsValue' \ + 'javax.sql.DataSource.yourDataSourceName.someOtherPropertyOfYourConnectionPoolAndDataSource=anotherValue' \ + java # ... +---- + +===== Example: `application.yaml` Classpath Resource + +Here is an example of some named data source configuration as might be +found in a `src/main/resources/application.yaml` classpath resource: + +[source,yaml] +---- +javax: + sql: + DataSource: + yourDataSourceName: + somePropertyOfYourConnectionPoolAndDataSource: itsValue + someOtherPropertyOfYourConnectionPoolAndDataSource: anotherValue +---- + +===== Example: Configuring the Oracle Universal Connection Pool and Oracle JDBC [[DS-Configuration-UCP-Oracle-Example]] + +This example presumes you have: + +* <> + +* <> + +This example, in Java properties file format, configures an Oracle +Universal Connection Pool-managed data source named `main` to +link:{oracle-jdbc-doc-base-url}/data-sources-and-URLs.html#GUID-C4F2CA86-0F68-400C-95DA-30171C9FB8F0[connect +to an Oracle Database] on `localhost` port `1521`, using the +`oracle.jdbc.poolOracleDataSource` vendor-supplied `DataSource`, with +a service name of `XE`, a `user` of `scott`, and a `password` of +`tiger`: + +[source,properties] +---- +javax.sql.DataSource.main.connectionFactoryClassName = oracle.jdbc.pool.OracleDataSource # <1> +javax.sql.DataSource.main.url = jdbc:oracle:thin://@localhost:1521/XE # <2> +javax.sql.DataSource.main.user = scott +javax.sql.DataSource.main.password = tiger +---- +<1> Why `connectionFactoryClassName`? See + link:{oracle-ucp-javadoc-base-url}/oracle/ucp/jdbc/PoolDataSourceImpl.html#setConnectionFactoryClassName_java_lang_String_)[`PoolDataSourceImpl#setConnectionFactoryClassName(String)`]. +<2> See + link:{oracle-jdbc-doc-base-url}/data-sources-and-URLs.html#GUID-EF07727C-50AB-4DCE-8EDC-57F0927FF61A[Thin-style + Service Name Syntax]. + +In general, the properties that can be set on the Oracle Universal +Connection Pool can be inferred from the "setter" methods found in +link:{oracle-ucp-javadoc-base-url}/oracle/ucp/jdbc/PoolDataSourceImpl.html[the +javadoc for the `PoolDataSourceImpl` class]. + +In general, the properties that can be set on the +link:{oracle-jdbc-javadoc-base-url}/oracle/jdbc/pool/OracleDataSource.html[`oracle.jdbc.pool.OracleDataSource`] +`DataSource` implementation can be inferred from the "setter" methods +found in +link:{oracle-jdbc-javadoc-base-url}/oracle/jdbc/pool/OracleDataSource.html[its +javadoc]. + +NOTE: link:{hikaricp-base-url}/src/main/java/com/zaxxer/hikari/util/PropertyElf.java#L46-L53[Unlike +HikariCP], the Oracle Universal Connection Pool does not distinguish +cleanly between configuration properties that affect _its_ behavior +and those that affect the behavior of the vendor-supplied `DataSource` +implementation whose connections it pools. For example, in the +example above it is not possible to tell that +link:{oracle-ucp-javadoc-base-url}/oracle/ucp/jdbc/PoolDataSourceImpl.html#setConnectionFactoryClassName_java_lang_String_[`connectionFactoryClassName` +is a property of the Oracle Universal Connection Pool], and +link:{oracle-jdbc-javadoc-base-url}/oracle/jdbc/datasource/impl/OracleDataSource.html#setUser_java_lang_String_[`user` +is a property of the `oracle.jdbc.pool.OracleDataSource` `DataSource` +implementation]. In some cases, the Oracle Universal Connection Pool +will +link:{oracle-ucp-javadoc-base-url}/oracle/ucp/jdbc/PoolDataSource.html#setUser_java_lang_String_[set +the given property on _both_ the connection pool itself _and_ on the +vendor-supplied `DataSource` it manages]. + +===== Example: Configuring the HikariCP Connection Pool and H2 [[DS-Configuration-HikariCP-H2-Example]] + +This example presumes you have: + +* <> + +* <> + +This example, in Java properties file format, configures a +HikariCP-managed data source named `test` to connect to an in-memory +H2 database named `unit-testing` with a `user` of `sa` and an empty +password: + +[source,properties] +---- +javax.sql.DataSource.test.dataSourceClassName = org.h2.jdbcx.JdbcDataSource # <1> +javax.sql.DataSource.test.dataSource.url = jdbc:h2:mem:unit-testing;DB_CLOSE_DELAY=-1 # <2> <3> +javax.sql.DataSource.test.dataSource.user = sa +javax.sql.DataSource.test.dataSource.password = +---- +<1> Why `dataSourceClassName`? See + https://github.com/brettwooldridge/HikariCP#essentials[HikariCP's + configuration documentation] for information about how HikariCP + separates configuration of the connection pool itself from + configuration of the vendor-supplied `DataSource`. +<2> Why `dataSource.`? See + link:{hikaricp-base-url}/src/main/java/com/zaxxer/hikari/util.PropertyElf.java#L47-49[`PropertyElf.java`, + lines 47–49]. +<3> See https://www.h2database.com/html/features.html#database_url[the + H2 database's documentation about its URL format]. + +HikariCP's configuration properties are described +https://github.com/brettwooldridge/HikariCP#gear-configuration-knobs-baby[on +its Github repository]. Properties that should be forwarded on to the +vendor-supplied `DataSource` +link:{hikaricp-base-url}/src/main/java/com/zaxxer/hikari/util/PropertyElf.java#L46-L53[are +prefixed with `dataSource.`] as seen in the example above. + +In general, the properties that can be set on the +https://www.h2database.com/javadoc/org/h2/jdbcx/JdbcDataSource.html[`org.h2.jdbcx.JdbcDataSource`] +vendor-supplied `DataSource` can be inferred from the "setter" methods +found in +https://www.h2database.com/javadoc/org/h2/jdbcx/JdbcDataSource.html[its +javadoc]. + +==== Usage [[DS-Usage]] + +You use Helidon MP's named data source integration in the same way, +regardless of your choices of vendor-supplied `DataSource` and +connection pool. + +To use Helidon MP's named data source integration in your application, +once it has been <> and +<>, create an ordinary +link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[`DataSource`]-typed +injection point in a +https://github.com/oracle/helidon/wiki/FAQ#how-do-i-make-a-class-a-cdi-bean[Java +class representing a CDI bean] somewhere in your application, +link:{jakarta-inject-javadoc-url}/jakarta/inject/named[annotated with +the name] of the data source you wish to use. + +Here is how to define such a field-backed injection point: + +[source,java] +---- +import javax.sql.DataSource; + +import jakarta.inject.Inject; +import jakarta.inject.Named; + +// ... + +@Inject // <1> +@Named("test") // <2> +private DataSource ds; // <3> +---- +<1> link:{jakarta-inject-javadoc-url}/jakarta/inject/inject[`@Inject`] + marks the field as an injection point. Its behavior is defined by + the link:{jakarta-inject-spec-url}[Jakarta Dependency Injection + specification]. +<2> link:{jakarta-inject-javadoc-url}/jakarta/inject/named[`@Named("test")`] + says to use the data source named `test` (as declared by the + <> of a named + data source configuration property). +<3> The field injection point has a type of + link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[`javax.sql.DataSource`], + and the field itselfmay be named anything you like. + +Here is how to define such a constructor parameter injection point: + +[source,java] +---- +import javax.sql.DataSource; + +import jakarta.inject.Inject; +import jakarta.inject.Named; + +// ... + +private final DataSource ds; // <1> + +@Inject // <2> +public SomeObject(@Named("test") DataSource ds) { // <3> + this.ds = ds; // <4> +} +---- +<1> This is the field whose value will be set in the constructor. +<2> link:{jakarta-inject-javadoc-url}/jakarta/inject/inject[`@Inject`] + marks the constructor as one containing parameter injection + points. Its behavior is defined by the + link:{jakarta-inject-spec-url}[Jakarta Dependency Injection + specification]. +<3> link:{jakarta-inject-javadoc-url}/jakarta/inject/named[`@Named("test")`] + says to use the data source named `test` (as declared by the + <> of a named + data source configuration property). The parameter injection + point has a type of + link:{jdk-javadoc-url}/java.sql/javax/sql/DataSource.html[`javax.sql.DataSource`], + and the parameter itself may be named anything you like. + +<4> The injected argument will never be `null`. + +== Jakarta Transactions (JTA) Integration [[JTA]] + +=== Overview + +Helidon MP's Jakarta Transactions integration integrates the +https://www.narayana.io/[Naryana transaction engine], an +implementation of the link:{jakarta-transactions-base-url}/[Jakarta +Transactions Specification], into Helidon MP. It lets you use +link:{jakarta-transactions-javadoc-url}/jakarta/transaction/transactional[`@jakarta.transaction.Transactional`] +to declare JTA transactions in your Java code. + +=== Maven Coordinates (JTA) [[JTA-Maven-Coordinates]] + +To include Helidon's JTA integration in your application: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* Ensure the following `` elements are present as child + elements of your project's `pom.xml` file's `` element: ++ +[source,xml] +---- + + jakarta.transaction + jakarta.transaction-api + provided + + + io.helidon.integrations.cdi + helidon-integrations-cdi-jta-weld + runtime + +---- +<1> The `scope` is `provided`, which ensures that the + link:{jakarta-transactions-javadoc-url}/jakarta/transaction/transactional[JTA + classes required for compilation] are available at compile time. +<2> The implementation of these API classes (provided by + https://narayana.io/[Narayana]) will be available at runtime. + +=== Configuration [[JTA-Configuration]] + +==== Overview + +Helidon MP's Jakarta Transactions integration does not require +configuration, but configuration is possible. Because configuration +is of the https://narayana.io/[underlying Narayana transaction +engine], any restrictions are those of the engine, not of Helidon +itself. + +Narayana, unlike Helidon MP, does not use MicroProfile Config, so its +configuration options are less flexible. + +Some common examples of Narayana configuration follow. + +==== Configuring the Object Store Directory + +Narayana features an object store directory which it uses to store +information about transaction outcomes. To set its location, you may +set the +https://www.narayana.io/docs/api/com/arjuna/ats/arjuna/common/ObjectStoreEnvironmentBean.html#setObjectStoreType-java.lang.String-[`ObjectStoreEnvironmentBean.objectStoreDir`] +system property to the full path of a writeable directory: + +[source,bash] +---- +java -DObjectStoreEnvironmentBean.objectStoreDir=/var/tmp # ... +---- + +See https://www.narayana.io/docs/project/index.html#d0e4013[Specifying +the object store location] for more information. + +==== Configuring the Default Transaction Manager Timeout + +To configure Narayana's +https://www.narayana.io/docs/api/com/arjuna/ats/arjuna/common/CoordinatorEnvironmentBean.html#setDefaultTimeout-int-[default +transaction manager timeout], set the +`com.arjuna.ats.arjuna.coordinator.defaultTimeout` system property to +an integral value in seconds: + +[source,bash] +---- +java -Dcom.arjuna.ats.arjuna.coordinator.defaultTimeout=60 # ... +---- + +For more on configuring Narayana, see +https://www.narayana.io/docs/project/index.html#chap-JBossJTA_Installation_Guide-Test_Chapter[Setting +Properties] in the Naryana documentation. + +=== Usage [[JTA-Usage]] + +To use Helidon MP's Jakarta Transactions integration, annotate a +method with the +link:{jakarta-transactions-javadoc-url}/jakarta/transaction/transactional[`jakarta.transaction.Transactional`] +annotation: + +[source,java] +---- +import jakarta.transaction.Transactional; + +// ... + +@Transactional // <1> +public String getGreeting(Integer id) { + // Use a JTA-aware facility to do something transactional here. <2> +} +---- +<1> The + link:{jakarta-transactions-javadoc-url}/jakarta/transaction/transactional[`@Transactional` + annotation] indicates that this method should be invoked in the + scope of a JTA transaction. **The object on which the method is + invoked must be one that Helidon MP's CDI container has created**, + i.e. it must be managed. + (link:{jakarta-cdi-spec-url}#implementation[CDI beans are + managed], as are + link:{jakarta-jaxrs-spec-url}#resource-classes[Jakarta RESTful Web + Services resource classes].) +<2> For + link:{jakarta-transactions-javadoc-url}/jakarta/transaction/transactional[`@Transactional`] + to have any effect, whatever is used inside the method must be + JTA-aware (such as a link:{jakarta-persistence-base-url}/[Jakarta + Persistence] object like a managed + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/entitymanager[`EntityManager`]). + +== Jakarta Persistence (JPA) [[JPA]] + +=== Overview + +Helidon MP's link:{jakarta-persistence-base-url}/[Jakarta Persistence] +integration allows you to interact with Jakarta Persistence (JPA) +objects as if your code were running in an application server, +handling automatic creation and management of objects such as +`EntityManager` and `EntityManagerFactory` instances. + +More pragmatically, it allows you to inject managed +link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/entitymanager[`EntityManager`] +instances using the +link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/persistencecontext[`@PersistenceContext`] +annotation. + +Jakarta Persistence is a Jakarta EE specification that describes, +among other things, how its implementations: + +1. Map Java objects to relational database tables + +2. Manage such persistent Java objects + +3. Interact with <> + +4. Interact with <> + +Jakarta Persistence may be used in an entirely application-managed +manner, which requires no integration at all. This +application-managed mode places the burden of error handling, thread +safety, transaction management, and other concerns on the user. +**This documentation does _not_ cover application-managed mode JPA.** + +Jakarta Persistence may also (preferably) be used in a fully +container-managed manner, which requires that a container, like +Helidon MP, handle error management, thread safety and transaction +management on behalf of the user. **This documentation covers this +container-managed mode of JPA exclusively.** + +Helidon MP's Jakarta Persistence integration comes with support for +two JPA implementations, known as _JPA providers_: + +1. link:{hibernate-doc-base-url}[Hibernate ORM] + +2. https://www.eclipse.org/eclipselink/documentation/[Eclipselink] + +In any given project, you use one or the other, but not both. + +How you set up Helidon MP's Jakarta Persistence integration differs +depending on which of these JPA providers you choose to use. + +Jakarta Persistence requires <> and makes +use of <>, so as you set up your project you +will need to understand: + +* <> + +* <> + +=== Project Setup [[JPA-Setup]] + +==== Setting Up a JPA Provider + +===== Overview + +While the Jakarta Persistence specification standardizes many aspects +around programming and usage, it deliberately leaves many required +setup and configuration aspects up to the JPA provider. You will need +to set up your project differently depending on which JPA provider you +choose. + +To set up Helidon MP's Jakarta Persistence integration in your +application to work with your chosen JPA provider, you must: + +1. <> + +2. <> + +3. Include the proper Jakarta Persistence-related dependencies + +4. Set up your project to generate and compile the + link:{jakarta-persistence-spec-url}#a6933[static metamodel] + +5. Set up your project for _static weaving_ + +Details and examples for each supported JPA provider are below. + +===== Maven Coordinates (Common) [[JPA-Maven-Coordinates]] + +To include the Jakarta Persistence APIs that you will need and to +include the core of Helidon's Jakarta Persistence integration: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies + are managed] + +* <> + +* <> + +* Ensure the following `` elements are present as child + elements of your project's `pom.xml` file's `` element: ++ +[source,xml] +---- + + jakarta.persistence + jakarta.persistence-api + provided + + + io.helidon.integrations.cdi + helidon-integrations-cdi-jpa + runtime + +---- +<1> The `scope` is `provided`, which ensures that the + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/package-summary.html[JPA + classes required for compilation] are available at compile time. +<2> The `scope` is `runtime`, which ensures that Helidon's core, + provider-independent Jakarta Persistence integration is available at + runtime. + +These `` elements do not set up a JPA provider. See +details below for the JPA provider you have chosen to use. + +===== Setting Up Static Metamodel Generation [[JPA-MetaModel]] + +To generate and compile the Jakarta Persistence static metamodel for +your application, regardless of whether you are using Hibernate ORM or +Eclipselink, make sure the `` element in the following code +snippet is present as a child element of the +`` element sequence as shown below: + +[source,xml] +---- + + + + + + + maven-compiler-plugin + + + default-compile + + + + org.hibernate.orm + hibernate-jpamodelgen + 6.1.1.Final + + + + + + + + + + + +---- +<1> This adds the `hibernate-jpamodelgen` jar, which contains a + link:{hibernate-javadoc-base-url}/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.html[Java + annotation processor that generates the static metamodel source + code], to the Java compiler's annotation processor path so that it + is active at compile time. +<2> Always check + https://search.maven.org/artifact/org.hibernate.orm/hibernate-jpamodelgen[Maven + Central] for up to date versions and make sure the version used is + that of Hibernate ORM itself. + +For more on the Hibernate ORM `hibernate-jpamodelgen` annotation +processor, see +https://hibernate.org/orm/tooling/#hibernate-metamodel-generator[Hibernate +Metamodel Generator] in Hibernate ORM's documentation. + +NOTE: Many parts of Hibernate ORM's documentation of this feature are +outdated. + +===== Maven Coordinates (Hibernate ORM) [[JPA-Hibernate-Maven-Coordinates]] + +To include Helidon's Jakarta Persistence-related integration for +Hibernate ORM: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* <> + +* Ensure the following `` elements are present as child + elements of your project's `pom.xml` file's `` + element: ++ +[source,xml] +---- + + io.helidon.integrations.cdi + helidon-integrations-cdi-eclipselink + runtime + +---- +<1> The `scope` is `runtime`, which ensures that Helidon MP's + Hibernate ORM integration is available at runtime. + +===== Setting Up Static Weaving (Hibernate ORM) [[JPA-Hibernate-Weaving]] + +Hibernate ORM can alter your classes' bytecode at build time to keep +track of changes made to objects participating in Jakarta Persistence +workflows. + +To set up this required static weaving for Hibernate ORM, ensure that +the following `` element is present as a child element of your +project's `pom.xml` file's `` element: + +[source,xml] +---- + + org.hibernate.orm.tooling + hibernate-enhance-maven-plugin + + + + + Statically enhance JPA entities for Hibernate + compile + + enhance + + + true + true + true + + + + +---- + +You must also +https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#appendix-legacy-bootstrap:~:text=Place%20a%20file%20named%20hibernate.properties%20in%20a%20root%20directory%20of%20the%20classpath[add +a file named `src/main/resources/META-INF/hibernate.properties`] with +the following line as its sole contents: + +.`src/main/resources/META-INF/hibernate.properties` +[source,properties] +---- +hibernate.bytecode.provider=none # <1> +---- +<1> The value of `none` instructs Hibernate ORM to + link:{hibernate-javadoc-base-url}/org/hibernate/cfg/Environment.html#BYTECODE_PROVIDER_NAME_NONE[not + attempt to perform weaving at runtime]. + +For more on the `hibernate-enhance-maven-plugin` in particular, see +link:{hibernate-doc-jboss-url}/userguide/html_single/Hibernate_User_Guide.html#tooling-maven[its +documentation]. + +For more on Hibernate ORM's bytecode enhancement (weaving) in general, +see +link:{hibernate-doc-jboss-url}/userguide/html_single/Hibernate_User_Guide.html#BytecodeEnhancement[Bytecode +Enhancement] in Hibernate ORM's documentation. + +For more on bytecode enhancement properties, see +link:{hibernate-doc-jboss-url}/userguide/html_single/Hibernate_User_Guide.html#configurations-bytecode-enhancement[Bytecode +Enhancement Properties] in Hibernate ORM's documentation. + +===== Maven Coordinates (Eclipselink) [[JPA-Eclipselink-Maven-Coordinates]] + +To include Helidon's Jakarta Persistence-related integration for +Eclipselink: + +* xref:../about/managing-dependencies.adoc[Ensure your dependencies are +managed] + +* <> + +* Ensure the following `` elements are present as child + elements of your project's `pom.xml` file's `` + element: ++ +[source,xml] +---- + + io.helidon.integrations.cdi + helidon-integrations-cdi-eclipselink + runtime + +---- +<1> The `scope` is `runtime`, which ensures that Helidon MP's + Eclipselink integration is available at runtime. + +===== Setting Up Static Weaving (Eclipselink) [[JPA-Eclipselink-Weaving]] + +Eclipselink can alter your classes' bytecode at build time to keep +track of changes made to objects participating in Jakarta Persistence +workflows. + +To set up this required static weaving for Eclipselink, ensure that +the following `` element is present as a child element of your +project's `pom.xml` file's `` element: + +[source,xml] +---- + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + weave + process-classes + + java + + + compile + org.eclipse.persistence.tools.weaving.jpa.StaticWeave + + -loglevel + INFO + -persistenceinfo + ${project.build.outputDirectory} + ${project.build.outputDirectory} + ${project.build.outputDirectory} + + + + + +---- +<1> Always check + https://search.maven.org/artifact/org.codehaus.mojo/exec-maven-plugin[Maven + Central] for up-to-date versions. + +For more on the Eclipselink static weaving command-line utility, see +https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Command_Line[Static +Weaving] in the Eclipselink documentation. + +=== Configuration [[JPA-Configuration]] + +To configure Helidon MP's Jakarta Persistence integration, you author +a +link:{jakarta-persistence-spec-url}#persistence-xml-file[`src/main/resources/META-INF/persistence.xml` +file]. It contains a mix of standardized elements and JPA +provider-specific properties. + +For details about the structure and syntax of the +`META-INF/persistence.xml` file, see +link:{jakarta-persistence-spec-url}#persistence-xml-file[persistence.xml +file] in the Jakarta Persistence specification. + +Fundamentally, a `META-INF/persistence.xml` file contains a collection +of _persistence units_. A persistence unit represents a collection of +entities in a relational database loosely coupled to a <> that knows how to connect to it. + +Your `src/main/resources/META-INF/persistence.xml` file must begin +(and end) with the following XML: + +.`src/main/resources/META-INF/persistence.xml` +[source,xml] +---- + + + + + +---- +<1> Helidon MP's Jakarta Persistence integration supports + link:{jakarta-persistence-base-url}/[Jakarta Persistence version + 3.0]. +<2> ` elements are listed here. + +==== Persistence Unit + +You list your application's persistence units as +`` child elements of the enclosing `` +element. Each `` element identifies a named +persistence unit that will correspond to an `EntityManager` in your +code, and represents a collection of entities in a relational database. + +===== Example: Persistence Unit Skeleton + +Here is a partial example of a persistence unit named `prod` with a +helpful description: + +.`src/main/resources/META-INF/persistence.xml` +[source,xml] +---- + + + + The production database + + + + + + +---- +<1> Because Helidon MP's JPA integration is for container-managed JPA, + the link:{jakarta-persistence-spec-url}#a12296[`transaction-type` + attribute] must in practice always be set to `JTA`. +<2> The order of subsequent child elements is significant and governed + by the + link:{jakarta-persistence-spec-url}#persistence-xml-schema[XML + schema]. + +NOTE: In most microservices, there will be only one persistence unit. + +TIP: A `` is represented in Jakarta Persistence as +an instance of the +link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/spi/persistenceunitinfo[`PersistenceUnitInfo`] +class. + +===== JTA Data Source [[JPA-JTA-Data-Source]] + +A persistence unit is always associated with exactly one <>. + +Because Helidon MP's Jakarta Persistence integration provides support +for container-managed JPA, and because container-managed JPA requires +Jakarta Transactions (JTA), the kind of named data source a +persistence unit is associated with is always a <> data +source. The `` element, a child of the +`` element, is how you link a persistence unit to a +<> you previously <>. + +====== Example: Persistence Unit with JTA Data Source + +Here is a partial example of a persistence unit named `prod`, with a +helpful description, linked with a JTA data source named `main`: + +.`src/main/resources/META-INF/persistence.xml` +[source,xml] +---- + + + + The production database + main + + + + + + +---- +<1> This links this persistence unit to a <> named + `main`, whose <> can + be found in a MicroProfile-Config-compatible location, as detailed + in the <> section + above. +<2> Other persistence unit characteristics go here. + +===== Classes + +A persistence unit lists the classes that should be managed and that +will take part in Jakarta Persistence workflows. You must list: + +1. link:{jakarta-persistence-spec-url}#a18[Entity classes] + +2. link:{jakarta-persistence-spec-url}#a487[Embeddable classes] + +3. link:{jakarta-persistence-spec-url}#mapped-superclasses[Mapped + superclasses] + +4. link:{jakarta-persistence-spec-url}#a2999[Converter classes] + +You use a +link:{jakarta-persistence-spec-url}#list-of-managed-classes[sequence +of `` elements] to do this. Each `` element contains +the fully-qualified class name of one of the types of managed classes +listed above. + +NOTE: There are link:{jakarta-persistence-spec-url}#a12305[other +mechanisms that can be used in a `META-INF/persistence.xml` file to +describe managed classes], but they may or may not be honored by a +given JPA provider. + +====== Example: Persistence Unit with Class Elements + +Here is a partial example of a persistence unit named `prod`, with a +helpful description, linked with a JTA data source named `main`, +containing two entity classes: + +.`src/main/resources/META-INF/persistence.xml` +[source,xml] +---- + + + + The production database + main + com.example.ExampleEntity0 + com.example.ExampleEntity1 + + + + + + +---- +<1> Each entity class is listed with a separate `` element, and + there is no containing `` element or similar. +<2> Other persistence unit characteristics go here. + +===== Properties + +Persistence units can have simple properties attached to them to +further configure the backing JPA provider. You use the +link:{jakarta-persistence-spec-url}#a12384[`` +element] to specify them. + +NOTE: Helidon MP's Jakarta Persistence integration is for +container-managed JPA, so the vendor-independent properties +link:{jakarta-persistence-spec-url}#a12384[described +in the specification] directly concerned with database connectivity +information, such as `jakarta.persistence.jdbc.url`, **do not apply** +and will be ignored if present. See <> section above for how a persistence unit is linked to a +<>. + +====== Example: Persistence Unit with Properties + +Here is a partial exmaple of a persistence unit named `prod`, with a +helpful description, linked with a JTA data source named `main`, +containing two entity classes, configuring a Hibernate ORM-specific +property: + +.`src/main/resources/META-INF/persistence.xml` +[source,xml] +---- + + + + The production database + main + com.example.ExampleEntity0 + com.example.ExampleEntity1 + + + + + + + +---- +<1> The name identifies a name present in the + <>. There is no need for any kind of + reserved prefix (like `java:comp/env`). +<2> This is a Hibernate ORM-specific property and will be properly + ignored if the JPA provider you have <> is + Eclipselink. See + link:{hibernate-doc-jboss-url}/userguide/html_single/Hibernate_User_Guide.html#configurations-logging[Statement + logging and statistics] in the Hibernate ORM documentation for + more details about the `hibernate.show_sql` property. +<3> This is an Eclipselink-specific property (and (a) is required and + (b) must be set to `false` if you are using Eclipselink), and will + be properly ignored if the JPA provider you have <> is Hibernate ORM. See + link:{eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm#weaving[weaving] + in the Eclipselink documentation for more details about the + `eclipselink.weaving` property. + +TIP: For an exhaustive list of Hibernate ORM-specific properties, see +link:{hibernate-doc-jboss-url}/userguide/html_single/Hibernate_User_Guide.html#configurations[Configurations] +in the Hibernate ORM documentation. + +TIP: For an exhaustive list of Eclipselink-specific properties, see +link:{eclipselink-doc-base-url}/jpa/extensions/persistenceproperties_ref.htm#sthref733[Persistence +Property Extensions Reference] in the Eclipselink documentation. + +=== Usage [[JPA-Usage]] + +To use Helidon MP's Jakarta Persistence integration, once you have +<> and <> your +project, you use the Jakarta Persistence APIs in almost the same +manner as if your project were deployed to a Jakarta EE server. + +Specifically, you: + +1. Annotate your managed classes (entities, mapped superclasses, etc.) + appropriately (using + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/entity[`@Entity`] + and similar annotations) + +2. Inject + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/entitymanager[`EntityManager`] + instances appropriately with the + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/persistencecontext[`@PersistenceContext` + annotation] + +3. Use an injected + link:{jakarta-persistence-javadoc-url}/jakarta.persistence/jakarta/persistence/entitymanager[`EntityManager`] + to work with your managed objects + +In addition, you <> to +declare transactional boundaries where appropriate. + +A full tutorial of Jakarta Persistence is _well_ beyond the scope of +this documentation. Consult +link:{jakarta-persistence-spec-url}#entities[the +specification] for details on how to map your entity classes to +relational database tables, and how to perform other related tasks. + +=== Examples [[JPA-Examples]] + +* link:{helidon-github-tree-url}/examples/integrations/cdi/pokemons[JPA + Pokemons Example] + +== References [[References]] + +* xref:../about/managing-dependencies.adoc[Managing Dependencies in Helidon MP] + +* xref:config/introduction.adoc[MicroProfile Config in Helidon MP] + +* link:{jdk-javadoc-url}/java.sql/java/sql/package-summary.html[JDBC + 4.3 Specification] + +* link:{hikaricp-base-url}#readme[HikariCP {version-lib-hikaricp} + documentation] + +* https://www.oracle.com/database/technologies/maven-central-guide.html[Developers + Guide For Oracle JDBC 21c on Maven Central] + +* link:{oracle-ucp-doc-base-url}/index.html[Oracle® Universal + Connection Pool Developer's Guide, Release 21c] + +* link:{oracle-ucp-javadoc-base-url}/index.html#[Oracle® Universal + Connection Pool Java API Reference, Release 21c] + +* link:{oracle-jdbc-doc-base-url}/index.html[Oracle® Database JDBC + Developer's Guide and Reference, Release 21c] + +* link:{oracle-jdbc-javadoc-base-url}/index.html[Oracle® Database JDBC + Java API Reference, Release 21c] + +* https://www.h2database.com/html/main.html[H2 Database Engine documentation] + +* link:{jakarta-transactions-spec-url}[Jakarta Transactions + {version-lib-jakarta-transaction-api} Specification] + +* link:{jakarta-transactions-javadoc-url}/[Jakarta Transactions + {version-lib-jakarta-transaction-api} API Reference] + +* https://www.narayana.io/docs/project/index.html[Narayana Project Documentation] + +* https://www.narayana.io/docs/api/index.html[Narayana API Reference] + +* link:{jakarta-persistence-spec-url}[Jakarta Persistence + {persistence-lib-jakarta-persistence-api} Specification] + +* link:{jakarta-persistence-javadoc-url}/[Jakarta Persistence + {persistence-lib-jakarta-persistence-api} API Reference] + +* link:{hibernate-doc-jboss-url}userguide/html_single/Hibernate_User_Guide.html[Hibernate + ORM User Guide] + +* https://www.eclipse.org/eclipselink/documentation/[Eclipselink documentation] diff --git a/docs/mp/reactivemessaging/aq.adoc b/docs/mp/reactivemessaging/aq.adoc index 93c0794e61c..71a806e2495 100644 --- a/docs/mp/reactivemessaging/aq.adoc +++ b/docs/mp/reactivemessaging/aq.adoc @@ -101,8 +101,8 @@ mp: type: queue ---- -Its also possible and preferable to refer to xref:../guides/datasource.adoc[configured datasource], -in our example xref:../extensions/cdi-datasource-ucp.adoc[Oracle UCP datasource]: +Its also possible and preferable to refer to xref:../persistence.adoc[configured datasource], +in our example xref:../persistence.adoc[Oracle UCP datasource]: [source,yaml] .Example of connector config with Oracle UCP datasource: diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 2de65cf0d6e..c25866206a6 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -274,9 +274,6 @@ backend: - "metrics.adoc" - "security-oidc.adoc" - "tracing.adoc" - - "datasource.adoc" - - "jta.adoc" - - "jpa.adoc" - "mp-tutorial.adoc" - "migration.adoc" - "maven-build.adoc" @@ -310,10 +307,7 @@ backend: value: "extension" sources: - "overview.adoc" - - "cdi-datasource-hikaricp.adoc" - - "cdi-datasource-ucp.adoc" - "cdi-jedis.adoc" - - "cdi-jta.adoc" - type: "PAGE" title: "CORS" source: "cors/cors.adoc" @@ -361,8 +355,8 @@ backend: - "jaxrs-client.adoc" - "reactive-routing.adoc" - type: "PAGE" - title: "JPA" - source: "jpa.adoc" + title: "Persistence" + source: "persistence.adoc" glyph: type: "icon" value: "dns" From 2c6ae3152bfa0f9742d5827eb5de06de3201db69 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Wed, 20 Jul 2022 10:04:30 +0200 Subject: [PATCH 46/51] Archetypes : Fix READMEs required jdk version (#4572) Signed-off-by: tvallin --- .../helidon/src/main/archetype/common/files/README.md.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archetypes/helidon/src/main/archetype/common/files/README.md.mustache b/archetypes/helidon/src/main/archetype/common/files/README.md.mustache index 938601a61e0..2e13680e1b3 100644 --- a/archetypes/helidon/src/main/archetype/common/files/README.md.mustache +++ b/archetypes/helidon/src/main/archetype/common/files/README.md.mustache @@ -8,7 +8,7 @@ {{.}} {{/run-comment}} -With JDK11+ +With JDK17+ ```bash mvn package java -jar target/{{artifactId}}.jar From 74f9a8f95281797666477d18d95daa1ffb0c91e2 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Wed, 20 Jul 2022 14:58:49 +0200 Subject: [PATCH 47/51] Microprofile Server Documentation (#4545) Signed-off-by: tvallin --- docs/config/config_reference.adoc | 1 + ...io_helidon_microprofile_server_Server.adoc | 55 +++ docs/mp/extensions/cdi-jedis.adoc | 2 +- docs/mp/guides/mp-tutorial.adoc | 4 +- docs/mp/guides/se-services.adoc | 4 +- docs/mp/introduction/introduction.adoc | 2 +- docs/mp/jaxrs/reactive-routing.adoc | 137 -------- docs/mp/jaxrs/server-configuration.adoc | 222 ------------ docs/mp/jaxrs/static-content.adoc | 52 --- docs/mp/server.adoc | 332 ++++++++++++++++++ docs/sitegen.yaml | 6 +- microprofile/server/pom.xml | 12 + .../helidon/microprofile/server/Server.java | 8 +- .../server/src/main/java/module-info.java | 1 + 14 files changed, 417 insertions(+), 421 deletions(-) create mode 100644 docs/config/io_helidon_microprofile_server_Server.adoc delete mode 100644 docs/mp/jaxrs/reactive-routing.adoc delete mode 100644 docs/mp/jaxrs/server-configuration.adoc delete mode 100644 docs/mp/jaxrs/static-content.adoc create mode 100644 docs/mp/server.adoc diff --git a/docs/config/config_reference.adoc b/docs/config/config_reference.adoc index bf779462f0e..be22a1d07cc 100644 --- a/docs/config/config_reference.adoc +++ b/docs/config/config_reference.adoc @@ -88,3 +88,4 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_webserver_WebServerTls.adoc[WebServerTls (webserver)] - xref:{rootdir}/config/io_helidon_tracing_zipkin_ZipkinTracerBuilder.adoc[ZipkinTracer (tracing.zipkin)] - xref:{rootdir}/config/io_smallrye_openapi_api_OpenApiConfig.adoc[io_smallrye_openapi_api_OpenApiConfig] +- xref:{rootdir}/config/io_helidon_microprofile_server_Server.adoc[Server (microprofile.server)] diff --git a/docs/config/io_helidon_microprofile_server_Server.adoc b/docs/config/io_helidon_microprofile_server_Server.adoc new file mode 100644 index 00000000000..0d42359ee59 --- /dev/null +++ b/docs/config/io_helidon_microprofile_server_Server.adoc @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +:description: Configuration of io.helidon.microprofile.server.Server +:keywords: helidon, config, io.helidon.microprofile.server.Server +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.microprofile.server.Server + += Server Configuration + +// tag::config[] + +[source,text] +.Type +---- +io.helidon.microprofile.server.Server +---- + +Configuration of Helidon Microprofile Server + + + +== Configuration options + + + + +Optional configuration options: +[cols="3,3,2,5"] + +|=== +|key |type |default value |description + +|`executor-service` |ExecutorService> |{nbsp} |Set a supplier of an executor service to use for tasks connected with application + processing (JAX-RS). +|`port` |int |{nbsp} |Configure listen port. +|`host` |string |{nbsp} |Configure listen host. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/mp/extensions/cdi-jedis.adoc b/docs/mp/extensions/cdi-jedis.adoc index 61d626accef..786ea139480 100644 --- a/docs/mp/extensions/cdi-jedis.adoc +++ b/docs/mp/extensions/cdi-jedis.adoc @@ -66,7 +66,7 @@ The extension implements this injection point by creating a link:{jedis-base-url}/jedis/JedisPool.html[JedisPool] object in the link:{jakarta-cdi-javadoc-url}/javax/enterprise/context/ApplicationScoped.html[application scope]. -You can configure the object using xref:../jaxrs/server-configuration.adoc[MicroProfile config]. +You can configure the object using xref:../server.adoc[MicroProfile config]. For example, the Jedis pool created above can be configured as follows: [source, properties] diff --git a/docs/mp/guides/mp-tutorial.adoc b/docs/mp/guides/mp-tutorial.adoc index 6aef133c14d..6e2c5ceb0ea 100644 --- a/docs/mp/guides/mp-tutorial.adoc +++ b/docs/mp/guides/mp-tutorial.adoc @@ -332,7 +332,7 @@ Rebuild the application and run it again. Notice that it now uses port 8080 as specified in the configuration file. TIP: You can learn more about options for configuring the Helidon Server on the - xref:../jaxrs/server-configuration.adoc[Configuring the Server] page. + xref:../server.adoc[Server Configuration] page. In addition to predefined server properties, application-specific configuration information can be added to this file. Add the `app.greeting` @@ -1143,7 +1143,7 @@ There were several links to more detailed information included in the * https://projects.eclipse.org/projects/technology.microprofile[Eclipse MicroProfile] * link:{jakarta-cdi-spec-url}[Contexts and Dependency Injection Specification] -* xref:../jaxrs/server-configuration.adoc[Configuring the Server] +* xref:../server.adoc[Server Configuration] * xref:../config/introduction.adoc[Config] * https://microprofile.io/project/eclipse/microprofile-metrics[MicroProfile Metrics Specification] * xref:metrics.adoc[Metrics Guide] diff --git a/docs/mp/guides/se-services.adoc b/docs/mp/guides/se-services.adoc index fc828770439..9edf866e8f5 100644 --- a/docs/mp/guides/se-services.adoc +++ b/docs/mp/guides/se-services.adoc @@ -31,7 +31,7 @@ For this 10 minute tutorial, you will need the following: include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] -Helidon MP supports xref:../jaxrs/reactive-routing.adoc[Reactive routing] which brings possibility for reusing +Helidon MP supports xref:../server.adoc[Reactive routing] which brings possibility for reusing `io.helidon.webserver.Service` implementations in Helidon MP. Such feature can be quite useful for common solutions for filtering, auditing, logging or augmenting REST endpoints in hybrid Helidon SE/MP environment. @@ -91,4 +91,4 @@ public class MyBean { You can leverage annotations: * @RoutingPath - path of the WebServer service -* @RoutingName - select routing when xref:../jaxrs/server-configuration.adoc#conf-additional-ports[serving requests on multiple ports] +* @RoutingName - select routing when xref:../server.adoc[serving requests on multiple ports] diff --git a/docs/mp/introduction/introduction.adoc b/docs/mp/introduction/introduction.adoc index 527356e371c..8b6a66fe12f 100644 --- a/docs/mp/introduction/introduction.adoc +++ b/docs/mp/introduction/introduction.adoc @@ -116,7 +116,7 @@ Expose health statuses of your applications. //JAXRS [CARD] .JAX-RS/Jersey -[icon=settings_ethernet,link=../jaxrs/server-configuration.adoc] +[icon=settings_ethernet,link=../server.adoc] -- Helidon MP supports building RESTful services using JAX-RS/Jersey. -- diff --git a/docs/mp/jaxrs/reactive-routing.adoc b/docs/mp/jaxrs/reactive-routing.adoc deleted file mode 100644 index 82bfa25ea1a..00000000000 --- a/docs/mp/jaxrs/reactive-routing.adoc +++ /dev/null @@ -1,137 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2019, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Reactive routing in Helidon MP -:description: Helidon MP reactive routing -:keywords: helidon, rest, reactive, WebServer, route, routing -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -Since Helidon 1.4 - -== Configuring a reactive route in Helidon MP - -Helidon MP Server will pick up CDI beans that implement the `io.helidon.webserver.Service` -interface and configure them with the underlying WebServer. - -This allows configuration of reactive routes to run alongside a JAX-RS application. - -The bean is expected to be either `ApplicationScoped` or `Dependent` and will be requested -only once during the boot of the `Server`. - -The bean will support injection of `ApplicationScoped` and `Dependent` scoped beans. -You cannot inject `RequestScoped` beans. Please use WebServer features to handle request -related objects. - -=== Customizing the reactive service -The service can be customized using annotations and/or configuration to be - -- registered on a specific path -- registered with a named routing - -==== Assigning a reactive service to named ports -Helidon has the concept of named routing. These correspond to the named ports -configured with WebServer. - -You can assign a reactive service to a named routing (and as a result to a named port) using -either an annotation or configuration (or both to override the value from annotation). - -===== Annotation `@RoutingName` -You can annotate a service bean with this annotation to assign it to a specific named routing, -that is (most likely) going to be bound to a specific port. - -The annotation has two attributes: -- `value` that defines the routing name -- `required` to mark that the routing name MUST be configured in Helidon server - -[source,java] -.`@RoutingName` example ----- -@ApplicationScoped -@RoutingName(value="admin", required="true") -@RoutingPath("/admin") -public class AdminService implements Service { -} ----- - -The example above will be bound to `admin` routing (and port) and will fail if such a port -is not configured. - -===== Configuration override of routing name - -For each service bean you can define the routing name and its required flag by specifying a configuration -option `bean-class-name.routing-name.name` and `bean-class-name.routing-name.required`. -For service beans produced with producer method replace `bean-class-name` with `class-name.producer-method-name`. - -Example (YAML) configuration for a service bean `io.helidon.examples.AdminService` that changes the -routing name to `management` and its required flag to `false`: - -[source,yaml] ----- -io.helidon.examples.AdminService: - routing-name: - name: "management" - required: false ----- - -==== Configuring a reactive service path - -Each service is registered on a path. If none is configured, then the service would be -configured on the root path. - -You can configure service path using an annotation or configuration (or both to override value from annotation) - -===== Annotation `@RoutingPath` - -You can configure `@RoutingPath` to define the path a service is registered on. - -===== Configuration override of routing path - -For each reactive service class you can define the routing path by specifying a configuration -option `class-name.routing-path.path`. - -Example (YAML) configuration for a class `io.helidon.example.AdminService` that changes the -routing path to `/management`: - -[source,yaml] ----- -io.helidon.examples.AdminService: - routing-path: - path: "/management" ----- - -=== Example configuration of reactive service - -A full configuration example (YAML): - -[source,yaml] ----- -server: - port: 8080 - sockets: - management: - port: 8090 - -io.helidon.examples.AdminService: - routing-name: - name: "management" - required: true - routing-path: - path: "/management" ----- diff --git a/docs/mp/jaxrs/server-configuration.adoc b/docs/mp/jaxrs/server-configuration.adoc deleted file mode 100644 index e132a536f8f..00000000000 --- a/docs/mp/jaxrs/server-configuration.adoc +++ /dev/null @@ -1,222 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2018, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Configuring the Server -:description: Helidon MicroProfile server configuration -:keywords: helidon, microprofile, micro-profile -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -By default, the server uses the MicroProfile Config, but you may also want to use Helidon configuration. - -== Configuring the Server - -There are 3 default MicroProfile Config sources: - -* `System.getProperties()` -* `System.getenv()` -* all `META-INF/microprofile-config.properties` files on the class path -* `application.yaml` on the classpath (read by default by Helidon Config) - -In this example, the configuration is in a file, and it includes Helidon configuration options. - -[source,properties] -.META-INF/microprofile-config.properties - Server configuration ----- -# default is localhost -server.host=some.host -# default is 7001 -server.port=7011 - -# Helidon configuration (optional) - -# Length of queue for incoming connections. Default is 1024 -server.backlog=512 -# TCP receive window. Default is 0 to use implementation default -server.receive-buffer=256 -# Socket timeout milliseconds - defaults to 0 (infinite) -server.timeout=30000 -# Defaults to Runtime.availableProcessors() -server.workers=4 ----- - -== Configuring TLS - -Helidon MP also supports custom TLS configuration. - -User is able to set following properties: - -* Server truststore - - Keystore with trusted certificates -* Private key and certificate - - Server certificate which will be used in TLS handshake - -[source,properties] -.META-INF/microprofile-config.properties - Server configuration ----- -#Truststore setup -server.tls.trust.keystore.resource.resource-path=server.p12 -server.tls.trust.keystore.passphrase=password -server.tls.trust.keystore.trust-store=true - -#Keystore with private key and server certificate -server.tls.private-key.keystore.resource.resource-path=server.p12 -server.tls.private-key.keystore.passphrase=password ----- - -Or the same configuration done in application.yaml file. - -[source,yaml] -.application.yaml - Server configuration ----- -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" ----- - -== Configuring additional ports [[conf-additional-ports]] - -Helidon MP can expose multiple ports, with the following limitations: - -- The default port is the port that serves your application (JAX-RS applications and resources) -- Other ports (in this example we configure one "admin" port) can be assigned endpoints that are exposed by Helidon components, - currently supported by MP Health and MP Metrics - -For this example, we will use a `yaml` file: - -- The port `7011` is the default port and will serve your application -- The port `8011` is named "admin" (this is an arbitrary name) -- MP Metrics are configured to use the "admin" port through the `routing` configuration (reference is by name) -- MP Health is configured the same way to reference the "admin" port - -[source,yaml] -.application.yaml - Server configuration ----- -server: - port: 7011 - host: "some.host" - sockets: - admin: - port: 8011 - bind-address: "some.host" - -metrics: - routing: "admin" - -health: - routing: "admin" ----- - -== Assigning JAX-RS applications to ports - -Since 1.4 - -Helidon has the concept of named routings. These correspond to the named ports -we have described in the previous section. - -You can assign a JAX-RS application to a named routing (and as a result to a named port) using -either an annotation or configuration (or both to override the value from annotation). - -=== Annotation `@RoutingName` -You can annotate an application with this annotation to assign it to a specific named routing, -that is (most likely) going to be bound to a specific port. - -The annotation has two attributes: -- `value` that defines the routing name -- `required` to mark that the routing name MUST be configured in Helidon server - -[source,java] -.`@RoutingName` example ----- -@ApplicationScoped -@ApplicationPath("/admin") -@RoutingName(value="admin", required="true") -public class AdminApplication extends Application { -} ----- - -The example above will be bound to `admin` routing (and port) and will fail if such a port -is not configured. - -=== Configuration override of routing name - -For each application class you can define the routing name and its required flag by specifying a configuration -option `class-name.routing-name.name` and `class-name.routing-name.required`. - -Example (YAML) configuration for a class `io.helidon.examples.AdminApplication` that changes the -routing name to `management` and its required flag to `false`: - -[source,yaml] ----- -io.helidon.examples.AdminApplication: - routing-name: - name: "management" - required: false ----- - -== Overriding JAX-RS application path - -Since Helidon 1.4 -In JAX-RS we can use `@ApplicationPath` to configure a path the JAX-RS application is available on. -As this is compiled into the source code, Helidon provides a way to override this using configuration. - -For each application class you can define the routing path by specifying a configuration -option `class-name.routing-path.path`. - -Example (YAML) configuration for a class `io.helidon.example.AdminApplication` that changes the -routing path to `/management`: - -[source,yaml] ----- -io.helidon.examples.AdminApplication: - routing-path: - path: "/management" ----- - -== Example configuration of JAX-RS application - -A full configuration example (YAML): - -[source,yaml] ----- -server: - port: 8080 - sockets: - management: - port: 8090 - -io.helidon.examples.AdminApplication: - routing-name: - name: "management" - required: true - routing-path: - path: "/management" ----- diff --git a/docs/mp/jaxrs/static-content.adoc b/docs/mp/jaxrs/static-content.adoc deleted file mode 100644 index 383d3ad7cb7..00000000000 --- a/docs/mp/jaxrs/static-content.adoc +++ /dev/null @@ -1,52 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2018, 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. - -/////////////////////////////////////////////////////////////////////////////// - -= Serving Static Content -:description: Helidon MicroProfile static content -:keywords: helidon, microprofile, micro-profile -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -You can serve static content from a location in a file system - or from the classpath. - -== Serving Static Content - -[source,properties] -.META-INF/microprofile-config.properties - File system static content ----- -# Location of content on file system -server.static.path.location=/var/www/html -# default is index.html -server.static.path.welcome=resource.html -# static content path - default is "/" -# server.static.path.context=/static-file ----- - -[source,properties] -.META-INF/microprofile-config.properties - Classpath static content ----- -# src/main/resources/WEB in your source tree -server.static.classpath.location=/WEB -# default is index.html -server.static.classpath.welcome=resource.html -# static content path - default is "/" -# server.static.classpath.context=/static-cp ----- - diff --git a/docs/mp/server.adoc b/docs/mp/server.adoc new file mode 100644 index 00000000000..535e31e3449 --- /dev/null +++ b/docs/mp/server.adoc @@ -0,0 +1,332 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += MicroProfile Server +:spec-name: MicroProfile Server +:description: {spec-name} support in Helidon MP +:keywords: helidon, mp, microprofile, server +:feature-name: MicroProfile Server +:microprofile-bundle: true +:rootdir: {docdir}/.. + +include::{rootdir}/includes/mp.adoc[] + +== Content + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon provides a MicroProfile server implementation (`io.helidon.microprofile.server`) that encapsulates the Helidon WebServer. + +== Maven-Coordinates + +To enable MicroProfile Server add the helidon-microprofile-core bundle dependency to your project's `pom.xml` +(see xref:../about/managing-dependencies.adoc[Managing Dependencies]). + +[source,xml] +---- + + io.helidon.microprofile.bundles + helidon-microprofile-core + +---- + +MicroProfile Server is already included in the bundle. + +If full control over the dependencies is required, and you want to minimize the quantity of the dependencies - +`Helidon MicroProfile Server` should be used. In this case the following dependencies should be included in your +project's `pom.xml`: + +[source,xml] +---- + + io.helidon.microprofile.server + helidon-microprofile-server + +---- + +== Usage + +Helidon Microprofile Server is used to collect and deploy JAX-RS application. It is recommended to instantiate the server directly +as is done in the xref:guides/quickstart.adoc[Helidon MP Quickstart example]. Note that the server lifecycle is bound to CDI. + +== API + +include::{rootdir}/config/io_helidon_microprofile_server_Server.adoc[leveloffset=+1,tag=config] + +The following table provides a brief description of routing annotations, including its parameters. More information +in `Configuring a reactive route` section. + +[cols="3,5", role="flex, sm10"] +|=== +|Annotation |Description + +a| +---- +@RoutingName( + value = "" + required = false +) +---- +| Binds a JAX-RS Application or Helidon Service to a specific (named) routing on `WebServer`.The routing should have +a corresponding named socket configured on the WebServer to run the routing on. + +a| +---- +@RoutingPath("/path") +---- +| Path of a Helidon Service to register with routing. + +|=== + +== Configuration + +By default, the server uses the MicroProfile Config, but you may also want to use xref:config/introduction.adoc[Helidon configuration]. + +In this example, the configuration is in a file, and it includes Helidon configuration options. + +include::{rootdir}/config/io_helidon_webserver_WebServer.adoc[leveloffset=+1,tag=config] + +== Examples + +=== Configuring TLS + +Helidon MP also supports custom TLS configuration. + +User is able to set following properties: + +* Server truststore +- Keystore with trusted certificates +* Private key and certificate +- Server certificate which will be used in TLS handshake + +[source,properties] +.META-INF/microprofile-config.properties - Server configuration +---- +#Truststore setup +server.tls.trust.keystore.resource.resource-path=server.p12 +server.tls.trust.keystore.passphrase=password +server.tls.trust.keystore.trust-store=true + +#Keystore with private key and server certificate +server.tls.private-key.keystore.resource.resource-path=server.p12 +server.tls.private-key.keystore.passphrase=password +---- + +Or the same configuration done in application.yaml file. + +[source,yaml] +.application.yaml - Server configuration +---- +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" +---- + +=== Configuring additional ports [[conf-additional-ports]] + +Helidon MP can expose multiple ports, with the following limitations: + +- The default port is the port that serves your application (JAX-RS applications and resources) +- Other ports (in this example we configure one "admin" port) can be assigned endpoints that are exposed by Helidon components, +currently supported by MP Health and MP Metrics + +For this example, we will use a `yaml` file: + +- The port `7011` is the default port and will serve your application +- The port `8011` is named "admin" (this is an arbitrary name) +- MP Metrics are configured to use the "admin" port through the `routing` configuration (reference is by name) +- MP Health is configured the same way to reference the "admin" port + +[source,yaml] +.application.yaml - Server configuration +---- +server: + port: 7011 + host: "some.host" + sockets: + admin: + port: 8011 + bind-address: "some.host" + +metrics: + routing: "admin" + +health: + routing: "admin" +---- + +=== Configuring a reactive route + +Helidon MP Server will pick up CDI beans that implement the `io.helidon.webserver.Service` +interface and configure them with the underlying WebServer. + +This allows configuration of reactive routes to run alongside a JAX-RS application. + +The bean is expected to be either `ApplicationScoped` or `Dependent` and will be requested +only once during the boot of the `Server`. + +The bean will support injection of `ApplicationScoped` and `Dependent` scoped beans. +You cannot inject `RequestScoped` beans. Please use WebServer features to handle request +related objects. + +==== Customizing the reactive service +The service can be customized using annotations and/or configuration to be + +- registered on a specific path +- registered with a named routing + +===== Assigning a reactive service to named ports +Helidon has the concept of named routing. These correspond to the named ports +configured with WebServer. + +You can assign a reactive service to a named routing (and as a result to a named port) using +either an annotation or configuration (or both to override the value from annotation). + +====== Annotation `@RoutingName` [[annotation-routing-name]] +You can annotate a service bean with this annotation to assign it to a specific named routing, +that is (most likely) going to be bound to a specific port. + +The annotation has two attributes: +- `value` that defines the routing name +- `required` to mark that the routing name MUST be configured in Helidon server + +[source,java] +.`@RoutingName` example +---- +@ApplicationScoped +@RoutingName(value="admin", required="true") +@RoutingPath("/admin") +public class AdminService implements Service { +} +---- + +The example above will be bound to `admin` routing (and port) and will fail if such a port +is not configured. + +====== Configuration override of routing name + +For each service bean you can define the routing name and its required flag by specifying a configuration +option `bean-class-name.routing-name.name` and `bean-class-name.routing-name.required`. +For service beans produced with producer method replace `bean-class-name` with `class-name.producer-method-name`. + +Example (YAML) configuration for a service bean `io.helidon.examples.AdminService` that changes the +routing name to `management` and its required flag to `false`: + +[source,yaml] +---- +io.helidon.examples.AdminService: + routing-name: + name: "management" + required: false +---- + +==== Configuring a reactive service path + +Each service is registered on a path. If none is configured, then the service would be +configured on the root path. + +You can configure service path using an annotation or configuration (or both to override value from annotation) + +===== Annotation `@RoutingPath` + +You can configure `@RoutingPath` to define the path a service is registered on. + +===== Configuration override of routing path + +For each reactive service class you can define the routing path by specifying a configuration +option `class-name.routing-path.path`. + +Example (YAML) configuration for a class `io.helidon.example.AdminService` that changes the +routing path to `/management`: + +[source,yaml] +---- +io.helidon.examples.AdminService: + routing-path: + path: "/management" +---- + +=== Serving Static Content + +[source,properties] +.META-INF/microprofile-config.properties - File system static content +---- +# Location of content on file system +server.static.path.location=/var/www/html +# default is index.html +server.static.path.welcome=resource.html +# static content path - default is "/" +# server.static.path.context=/static-file +---- + +[source,properties] +.META-INF/microprofile-config.properties - Classpath static content +---- +# src/main/resources/WEB in your source tree +server.static.classpath.location=/WEB +# default is index.html +server.static.classpath.welcome=resource.html +# static content path - default is "/" +# server.static.classpath.context=/static-cp +---- + + +=== Example configuration of routing + +A full configuration example (YAML): + +[source,yaml] +---- +server: + port: 8080 + sockets: + management: + port: 8090 + +io.helidon.examples.AdminApplication: + routing-name: + name: "management" + required: true + routing-path: + path: "/management" +---- + +== Reference + +* link:https://helidon.io/docs/v2/apidocs/io.helidon.microprofile.server/module-summary.html[Helidon MicroProfile Server Javadoc] +* link:https://github.com/oracle/helidon/tree/master/microprofile/server[Helidon MicroProfile Server on GitHub] diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index c25866206a6..17670ef0a27 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -348,12 +348,9 @@ backend: type: "icon" value: "settings_ethernet" sources: - - "server-configuration.adoc" - "application-configuration.adoc" - - "static-content.adoc" - "jaxrs-applications.adoc" - "jaxrs-client.adoc" - - "reactive-routing.adoc" - type: "PAGE" title: "Persistence" source: "persistence.adoc" @@ -454,6 +451,9 @@ backend: glyph: type: "icon" value: "sync_alt" + - type: "PAGE" + title: "Server" + source: "server.adoc" - type: "PAGE" title: "AOT" source: "aot.adoc" diff --git a/microprofile/server/pom.xml b/microprofile/server/pom.xml index 2a684cab3ab..56c33515f45 100644 --- a/microprofile/server/pom.xml +++ b/microprofile/server/pom.xml @@ -32,6 +32,18 @@ Server of the microprofile implementation + + io.helidon.config + helidon-config-metadata-processor + provided + true + + + io.helidon.config + helidon-config-metadata + provided + true + io.helidon.microprofile.cdi helidon-microprofile-cdi diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/Server.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/Server.java index 96a659e0cdc..3775df8b5f1 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/Server.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/Server.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -25,6 +25,8 @@ import io.helidon.common.configurable.ServerThreadPoolSupplier; import io.helidon.common.context.Contexts; +import io.helidon.config.metadata.Configured; +import io.helidon.config.metadata.ConfiguredOption; import io.helidon.config.mp.MpConfig; import io.helidon.config.mp.MpConfigSources; import io.helidon.microprofile.cdi.HelidonContainer; @@ -126,6 +128,7 @@ static Builder builder() { /** * Builder to build {@link Server} instance. */ + @Configured(prefix = "server", description = "Configuration of Helidon Microprofile Server") final class Builder { private static final Logger STARTUP_LOGGER = Logger.getLogger("io.helidon.microprofile.startup.builder"); @@ -266,6 +269,7 @@ private Server doBuild() { * @param host hostname * @return modified builder */ + @ConfiguredOption public Builder host(String host) { this.host = host; return this; @@ -294,6 +298,7 @@ public Builder basePath(String basePath) { * service * @return updated builder instance */ + @ConfiguredOption(key = "executor-service") public Builder defaultExecutorServiceSupplier(Supplier supplier) { this.defaultExecutorService = supplier; return this; @@ -305,6 +310,7 @@ public Builder defaultExecutorServiceSupplier(Supplier Date: Wed, 20 Jul 2022 14:15:04 -0500 Subject: [PATCH 48/51] remove refs (#4579) --- .../src/main/java/io/helidon/webserver/Router.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/Router.java b/webserver/webserver/src/main/java/io/helidon/webserver/Router.java index 0042a2a5e93..a4b4bfd3c45 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/Router.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/Router.java @@ -68,7 +68,7 @@ interface Builder extends RouterBuilder, io.helidon.common.Builder type of the builder */ @@ -76,7 +76,7 @@ interface RouterBuilder> { /** * Add a new routing to this router. * - * @param routing routing to add, such as {@code io.helidon.nima.webserver.http.HttpRouting} + * @param routing routing to add * @return updated builder */ B addRouting(Routing routing); @@ -84,7 +84,7 @@ interface RouterBuilder> { /** * Add a new routing to this router. * - * @param routing routing to add, such as {@code io.helidon.nima.webserver.http.HttpRouting} + * @param routing routing to add * @return updated builder */ default B addRouting(Supplier routing) { From eb862e7dadc421ca31e6070f8a5986750bc7191e Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Thu, 21 Jul 2022 09:01:27 +0200 Subject: [PATCH 49/51] Generate docker files for quickstart and database (#4581) Signed-off-by: tvallin --- .../src/main/archetype/common/presets.xml | 6 ++--- .../archetype/mp/database/database-mp.xml | 5 ----- .../mp/quickstart/files/README.native.md | 22 ------------------- .../archetype/mp/quickstart/quickstart-mp.xml | 3 --- .../main/archetype/se/custom/custom-se.xml | 2 +- .../archetype/se/database/database-se.xml | 5 ----- .../se/quickstart/files/README.native.md | 22 ------------------- .../archetype/se/quickstart/quickstart-se.xml | 4 +--- 8 files changed, 5 insertions(+), 64 deletions(-) delete mode 100644 archetypes/helidon/src/main/archetype/mp/quickstart/files/README.native.md delete mode 100644 archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md diff --git a/archetypes/helidon/src/main/archetype/common/presets.xml b/archetypes/helidon/src/main/archetype/common/presets.xml index c659040f635..5f4fe1e1705 100644 --- a/archetypes/helidon/src/main/archetype/common/presets.xml +++ b/archetypes/helidon/src/main/archetype/common/presets.xml @@ -32,9 +32,9 @@ false false - false - false - false + true + true + true false false diff --git a/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml b/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml index b758085a393..3ac8f035b43 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/database/database-mp.xml @@ -29,11 +29,6 @@ Helidon MP Database Helidon MP application that uses JPA with an in-memory H2 database. - - - diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/files/README.native.md b/archetypes/helidon/src/main/archetype/mp/quickstart/files/README.native.md deleted file mode 100644 index 8501b9435dc..00000000000 --- a/archetypes/helidon/src/main/archetype/mp/quickstart/files/README.native.md +++ /dev/null @@ -1,22 +0,0 @@ -## Building a Native Image - -Make sure you have GraalVM locally installed: - -``` -$GRAALVM_HOME/bin/native-image --version -``` - -Build the native image using the native image profile: - -``` -mvn package -Pnative-image -``` - -This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a while to complete. -Once it completes start the application using the native executable (no JVM!): - -``` -./target/{{artifactId}} -``` - -Yep, it starts fast. You can exercise the application’s endpoints as before. \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml index 158b5d76deb..ba96138348f 100644 --- a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml @@ -38,9 +38,6 @@ - - - jakarta.ws.rs.client.Entity jakarta.ws.rs.core.MediaType diff --git a/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml b/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml index f66056afb29..02ba5e240ba 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml +++ b/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml @@ -46,7 +46,7 @@ - System.out.println("WEB server is up! http://localhost:" + ws.port() + "/simple-greet"); + System.out.println("WEB server is up! http://localhost:" + ws.port() + "/simple-greet"); diff --git a/archetypes/helidon/src/main/archetype/se/database/database-se.xml b/archetypes/helidon/src/main/archetype/se/database/database-se.xml index 021eb0cffd5..f6e93a69cab 100644 --- a/archetypes/helidon/src/main/archetype/se/database/database-se.xml +++ b/archetypes/helidon/src/main/archetype/se/database/database-se.xml @@ -31,11 +31,6 @@ - - - diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md b/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md deleted file mode 100644 index 8501b9435dc..00000000000 --- a/archetypes/helidon/src/main/archetype/se/quickstart/files/README.native.md +++ /dev/null @@ -1,22 +0,0 @@ -## Building a Native Image - -Make sure you have GraalVM locally installed: - -``` -$GRAALVM_HOME/bin/native-image --version -``` - -Build the native image using the native image profile: - -``` -mvn package -Pnative-image -``` - -This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a while to complete. -Once it completes start the application using the native executable (no JVM!): - -``` -./target/{{artifactId}} -``` - -Yep, it starts fast. You can exercise the application’s endpoints as before. \ No newline at end of file diff --git a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml index b115f82fecd..6949f8c097d 100644 --- a/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml +++ b/archetypes/helidon/src/main/archetype/se/quickstart/quickstart-se.xml @@ -33,15 +33,13 @@ - - - + System.out.println("WEB server is up! http://localhost:" + ws.port() + "/greet"); From 0c8ab9d614511e63983c8f108de6ea6d5a1430ad Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 21 Jul 2022 00:53:14 +0200 Subject: [PATCH 50/51] JEP-290 documentation Signed-off-by: Tomas Langer --- docs/includes/security/jep-290.adoc | 92 +++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/includes/security/jep-290.adoc diff --git a/docs/includes/security/jep-290.adoc b/docs/includes/security/jep-290.adoc new file mode 100644 index 00000000000..37990786154 --- /dev/null +++ b/docs/includes/security/jep-290.adoc @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/../..] +:description: Helidon Security Java Serialization (JEP-290) +:keywords: helidon, security, serialization, java, JEP-290 +:metainf: META-INF/helidon/serial-config.properties + += Deserialization setup + +link:https://openjdk.org/jeps/290[JEP-290] brought support for deserialization filters to Java programming language. +Such filtering allows us to control which classes may be deserialized using Java serialization. + +Helidon default settings forbids any deserialization except for patterns defined in a `pattern` +property of any `{metainf}` on classpath. The patterns are semicolon delimited strings, such as `io.myapp.**;java.util.HashMap` (any subpackage of `io.myapp` and class `java.util.HashMap`). +Helidon will *always* add a deny-all filter pattern to the end of the pattern string (to make sure we exclude any unspecified class - we only operate on whitelists) + +These defaults can be modified either through system properties, or programmatically. + +== System property configuration +The following system properties can be used to control deserialization in Helidon: + +.System properties +[cols="3,3,5a"] + +|=== +|property |default value |description + +|`helidon.serialFilter.pattern` |`!*` |Filter pattern to use, deny all is always added +|`helidon.serialFilter.ignoreFiles` |`false` |Whether to ignore files `{metainf}` in libraries on the classpath +|`helidon.serialFilter.failure.action` |`FAIL` |Action to do when the configuration of global filter exists and is not consistent with our security expectations (e.g. contains a pattern to include all). + +Options: + +- `FAIL` - throw an exception to terminate startup +- `WARN` - log a warning +- `IGNORE` - ignore this and silently continue + +|`helidon.serialFilter.missing.action` |`CONFIGURE` |Action to do when there is no global configuration. + +Options: + +- `CONFIGURE` - configure Helidon defaults +- `FAIL` - throw an exception to terminate startup +- `WARN` - log a warning +- `IGNORE` - ignore this and silently continue + +|`helidon.serialFilter.trace` |`NONE` |Tracing configuration for deserialization. Controls what information (if any) will be logged to a logger `io.helidon.common.SerializationConfig.TracingObjectInputFilter` in `INFO` log level. + +Options: + +- `NONE` - do not trace +- `BASIC` - trace only classes, and only once per class +- `FULL` - trace all deserialization filter requests + +|=== + +== Programmatic configuration + +Custom `SerializationConfig` may be registered, but it *must* be done before Helidon server is started. + +[source,java] +.Configure custom Helidon serialization config +---- +SerializationConfig.builder() + .traceSerialization(SerializationConfig.TraceOption.BASIC) <1> + .filterPattern(MyType.class.getName()) <2> + .ignoreFiles(true) <3> + .onWrongConfig(SerializationConfig.Action.IGNORE) <4> + .build() + .configure(); <5> +---- +<1> Trace first instance of each class that is deserialized +<2> Configure a single class filter pattern (only allows deserialization of class `MyType` +<3> Ignore files defined in {metainf} +<4> In case there is an existing global serialization configuration on JDK, ignore it and continue (global filter cannot be reconfigured) +<5> Configure this serialization config as the default for this JVM \ No newline at end of file From e604b46b1c5beca9da43b5d4d964ac1f3f14a0b0 Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Thu, 21 Jul 2022 09:30:50 -0700 Subject: [PATCH 51/51] Integrate Helidon Build Tools v3.0.0-RC3. (#4580) * Integrate Helidon Build Tools v3.0.0-RC3. Move the archetype input filters to a separate properties file. * Fix media support and broken permutations. --- applications/pom.xml | 4 +- archetypes/helidon/filters.properties | 123 ++++++++++++++++++ archetypes/helidon/pom.xml | 46 +------ .../src/main/archetype/common/extra.xml | 2 +- .../archetype/common/files/pom.xml.mustache | 1 - .../src/main/archetype/common/media.xml | 78 +++++++---- .../main/archetype/common/observability.xml | 18 +-- .../main/archetype/mp/custom/custom-mp.xml | 11 +- .../src/main/archetype/mp/custom/database.xml | 1 - .../main/java/__pkg__/Pokemon.java.mustache | 2 + .../archetype/mp/quickstart/quickstart-mp.xml | 4 +- .../java/__pkg__/Message.java.json.mustache | 10 +- .../java/__pkg__/WebClientMain.java.mustache | 8 +- .../__pkg__/WebClientMainTest.java.mustache | 5 + dependencies/pom.xml | 5 + .../helidon-standalone-quickstart-mp/pom.xml | 4 +- .../helidon-standalone-quickstart-se/pom.xml | 4 +- pom.xml | 4 +- 18 files changed, 227 insertions(+), 103 deletions(-) create mode 100644 archetypes/helidon/filters.properties diff --git a/applications/pom.xml b/applications/pom.xml index fe94fc51a28..11f735c69a9 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -47,8 +47,8 @@ 3.1.2 1.6.0 3.0.0-M5 - 3.0.0-RC2 - 3.0.0-RC2 + 3.0.0-RC3 + 3.0.0-RC3 3.0.2 1.5.0.Final 0.5.1 diff --git a/archetypes/helidon/filters.properties b/archetypes/helidon/filters.properties new file mode 100644 index 00000000000..e28316a0176 --- /dev/null +++ b/archetypes/helidon/filters.properties @@ -0,0 +1,123 @@ +# +# 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. +# + +# This file contains permutation input filters, the property names are not used only the values. +# +# The number of computed permutations can be very large and quickly become un-manageable. +# The build will output warnings when the number of computed permutations for a node is > 150000. +# Filters are used to restrain the computed permutations. +# +# Filters are taken into account when they evaluate successfully. If a variable is unresolved, the result is ignored. +# A filter that needs to restrain a specific combination should only reference variables in the scope of the target node. + +# never combine multipart with other media options +multipart=${media} == 'multipart' || !(${media} contains 'multipart') + +# group metrics, tracing and health together +observability=!(${metrics} || ${tracing} || ${health}) || \ + (${metrics} && ${tracing} && ${health}) + +# force health.builtin=true +health=!${health} || (${health.builtin}) + +# force metrics.builtin=true +metrics=!${metrics} || (${metrics.builtin}) + +# force metrics.provider='microprofile' when tracing=true +tracing=!${tracing} || (${tracing} && ${metrics.provider} == 'microprofile') + +# group extra options +extra=${extra} == [] || ${extra} == ['cors', 'webclient', 'fault-tolerance'] + +# group docker, k8s and v8o +packaging=!(${docker} || ${k8s} || ${v8o}) || (${docker} && ${k8s} && ${v8o}) + +# force docker.native-image=true and docker.jlink-image=true when docker=true +docker=!${docker} || (${docker.native-image} && ${docker.jlink-image}) + +# force single option for security.atn +security-atn=\ + ${security.atn} == ['oidc'] || \ + ${security.atn} == ['jwt'] || \ + ${security.atn} == ['google'] || \ + ${security.atn} == ['http-signature'] + +# only combine security.atz with security.atn=oidc +security-atz=\ + ${security.atz} == [] || \ + (${security.atz} == 'abac' && ${security.atn} == 'oidc') + +# do not combine media and security +security-media=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${security} && ${media} == []) || !${security}) + +# do not combine metrics and media +metrics-media=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${metrics} && ${media} == []) || !${metrics}) + +# do not combine metrics and security +security-metrics=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${metrics} && !${security}) || !${metrics}) + +# do not combine docker and media +docker-media=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${docker} && ${media} == []) || !${docker}) + +# do not combine docker and security +docker-security=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${docker} && !${security}) || !${docker}) + +# do not combine docker and tracing +docker-tracing=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${docker} && !${tracing}) || !${docker}) + +# do not combine docker and extra +docker-extra=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (${docker} && ${extra} != []) || !${docker}) + +# do not combine security and extra +extra-security=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + (!${security} && ${extra} != []) || (${security} && ${extra} == [])) + +# do not combine custom and db +custom-db=\ + ${app-type} != 'custom' || (${app-type} == 'custom' && \ + !${db}) + +# do not combine media.json-lib=jackson when db.auto-ddl=true +ddl-media=\ + ${app-type} != 'database' || (${app-type} == 'database' && \ + (${db.auto-ddl} && ${media.json-lib} == 'jackson') || \ + (!${db.auto-ddl} && ${media.json-lib} != 'jackson')) + +# do not combine media.json-lib=jackson when db.cp=hikaricp +hikaricp-media=\ + ${app-type} != 'database' || (${app-type} == 'database' && \ + (${db.cp} == 'hikaricp' && ${media.json-lib} == 'jackson') || \ + (${db.cp} != 'hikaricp' && ${media.json-lib} != 'jackson')) + +# do not combine app-type=database when health=true +db-health=\ + ${app-type} != 'database' || (${app-type} == 'database' && \ + !${health}) diff --git a/archetypes/helidon/pom.xml b/archetypes/helidon/pom.xml index 93408973f3f..579e57b7b44 100644 --- a/archetypes/helidon/pom.xml +++ b/archetypes/helidon/pom.xml @@ -54,51 +54,7 @@ - - - - - - ${media} == 'multipart' || !(${media} contains 'multipart') - - !(${metrics} || ${tracing} || ${health}) || (${metrics} && ${tracing} && ${health}) - - !${health} || (${health.builtin}) - - !${metrics} || (${metrics.builtin}) - - !${tracing} || (${tracing} && ${metrics.provider} == 'microprofile') - - ${extra} == [] || (${extra} contains 'cors' && ${extra} contains 'webclient' && ${extra} contains 'fault-tolerance') - - !(${docker} || ${k8s} || ${v8o}) || (${docker} && ${k8s} && ${v8o}) - - !${docker} || (${docker.native-image} && ${docker.jlink-image}) - - ${security.atn} == 'oidc' || ${security.atn} == 'jwt' || ${security.atn} == 'google' || ${security.atn} == 'http-signature' - - (${security.atz} == 'abac' && ${security.atn} == 'oidc') || ${security.atz} == 'none' - - (${app-type} == 'custom' && ${security} == 'true' && ${media} == 'none') || ${security} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${metrics} == 'true' && ${media} == 'none') || ${metrics} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${metrics} == 'true' && ${security} == 'false') || ${metrics} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${docker} == 'true' && ${media} == 'none') || ${docker} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${docker} == 'true' && ${security} == 'false') || ${docker} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${docker} == 'true' && ${tracing} == 'false') || ${docker} == 'false' || ${app-type} != 'custom' - - (${app-type} == 'custom' && ${db} == 'false') || ${app-type} != 'custom' - - (${app-type} == 'database' && ${db.auto-ddl} == 'true') && ${media.json-lib} == 'jackson' || (${db.auto-ddl} == 'false' && ${media.json-lib} != 'jackson') || ${app-type} != 'database' - - (${app-type} == 'database' && ${db.cp} == 'hikaricp') && ${media.json-lib} == 'jackson' || (${db.cp} != 'hikaricp' && ${media.json-lib} != 'jackson' ) || ${app-type} != 'database' - - (${app-type} == 'database' && ${health} == 'false') || ${app-type} != 'database' - + filters.properties diff --git a/archetypes/helidon/src/main/archetype/common/extra.xml b/archetypes/helidon/src/main/archetype/common/extra.xml index 1f16cdde5e1..edf47467171 100644 --- a/archetypes/helidon/src/main/archetype/common/extra.xml +++ b/archetypes/helidon/src/main/archetype/common/extra.xml @@ -23,7 +23,7 @@ - @@ -59,25 +65,36 @@ - + com.fasterxml.jackson.core jackson-databind - + + org.glassfish.jersey.media + jersey-media-json-jackson + runtime + + io.helidon.media helidon-media-jackson - + io.helidon.media.jackson.JacksonSupport - + io.helidon.media.jackson.JacksonSupport - + + io.helidon.media.jackson.JacksonSupport + + - + + + + @@ -87,25 +104,36 @@ - + jakarta.json.bind jakarta.json.bind-api - + + org.glassfish.jersey.media + jersey-media-json-binding + runtime + + io.helidon.media helidon-media-jsonb - + io.helidon.media.jsonb.JsonbSupport - + io.helidon.media.jsonb.JsonbSupport - + + io.helidon.media.jsonb.JsonbSupport + + - + + + + @@ -118,38 +146,38 @@ - + org.glassfish.jersey.media jersey-media-multipart - + io.helidon.media helidon-media-multipart - + io.helidon.media helidon-media-jsonp - + io.helidon.media.jsonp.JsonpSupport - + io.helidon.media.jsonp.JsonpSupport - + io.helidon.media.multipart.MultiPartSupport - + - + - + - + @@ -163,6 +191,10 @@ + true + false + true + false ${media.json-lib} true diff --git a/archetypes/helidon/src/main/archetype/common/observability.xml b/archetypes/helidon/src/main/archetype/common/observability.xml index c1803e26907..2c3512ab7d7 100644 --- a/archetypes/helidon/src/main/archetype/common/observability.xml +++ b/archetypes/helidon/src/main/archetype/common/observability.xml @@ -56,15 +56,15 @@ curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics ]]> - + org.eclipse.microprofile.metrics microprofile-metrics-api - + io.helidon.microprofile.metrics helidon-microprofile-metrics - + io.helidon.metrics helidon-metrics @@ -328,7 +328,7 @@ allRequests_total 0.0 - + io.helidon.metrics helidon-metrics @@ -396,15 +396,15 @@ curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics - + io.helidon.microprofile.health helidon-microprofile-health - + io.helidon.health helidon-health - + io.helidon.health helidon-health-checks @@ -479,7 +479,7 @@ curl -s -X GET http://localhost:8080/health - + io.helidon.tracing helidon-tracing-jaeger @@ -542,7 +542,7 @@ tracing: - + io.helidon.tracing helidon-tracing-zipkin diff --git a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml index d7982d0318c..ce5c780c0a0 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml @@ -78,19 +78,10 @@ ]]> - + io.helidon.microprofile.bundles helidon-microprofile-core - - org.glassfish.jersey.media - jersey-media-json-binding - runtime - - - io.helidon.webclient - helidon-webclient - diff --git a/archetypes/helidon/src/main/archetype/mp/custom/database.xml b/archetypes/helidon/src/main/archetype/mp/custom/database.xml index 67190a76383..8085030f32c 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/database.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/database.xml @@ -445,7 +445,6 @@ docker run --rm --name xe -p 1521:1521 -p 8888:8080 -e ORACLE_PWD=oracle wnamele org.hibernate.validator hibernate-validator - 7.0.2.Final runtime diff --git a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache index 3c7085ccad9..a1f0dbdaa89 100644 --- a/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache +++ b/archetypes/helidon/src/main/archetype/mp/database/files/src/main/java/__pkg__/Pokemon.java.mustache @@ -1,6 +1,7 @@ package {{package}}; +{{#media-json-jackson}}import com.fasterxml.jackson.annotation.JsonIgnore;{{/media-json-jackson}} import jakarta.persistence.Access; import jakarta.persistence.AccessType; import jakarta.persistence.Basic; @@ -61,6 +62,7 @@ public class Pokemon { this.name = name; } +{{#media-json-jackson}} @JsonIgnore{{/media-json-jackson}} @ManyToOne public PokemonType getPokemonType() { return pokemonType; diff --git a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml index ba96138348f..b8ca8ba9618 100644 --- a/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/quickstart/quickstart-mp.xml @@ -92,8 +92,8 @@ - io.helidon.microprofile.bundles - helidon-microprofile + io.helidon.microprofile.openapi + helidon-microprofile-openapi diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache index 16571f6ab8c..4efa0bbde8a 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/Message.java.json.mustache @@ -1,6 +1,11 @@ package {{package}}; +{{#media-json-jackson}} +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +{{/media-json-jackson}} + public class Message { private String message; @@ -22,7 +27,10 @@ public class Message { this.greeting = greeting; } +{{#media-json-jackson}} + @JsonInclude(Include.NON_NULL) +{{/media-json-jackson}} public String getGreeting() { return this.greeting; } -} \ No newline at end of file +} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache index ed5c9dc2029..fbc70455091 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/WebClientMain.java.mustache @@ -3,7 +3,9 @@ package {{package}}; import io.helidon.common.reactive.Single; import io.helidon.config.Config; import io.helidon.config.ConfigValue; -import io.helidon.media.jsonp.JsonpSupport; +{{#WebClient-media-imports}} +import {{.}}; +{{/WebClient-media-imports}} import io.helidon.webclient.WebClient; public class WebClientMain { @@ -34,7 +36,9 @@ public class WebClientMain { WebClient webClient = WebClient.builder() .baseUri(url) .config(config.get("client")) - .addMediaSupport(JsonpSupport.create()) +{{#WebClient-builder}} + {{.}} +{{/WebClient-builder}} .build(); performGetMethod(webClient).await(); diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache index d94286c7e1f..b814b3e92ed 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/WebClientMainTest.java.mustache @@ -36,7 +36,12 @@ public class WebClientMainTest { @Test public void getSimpleGreetTest() throws Exception { WebClientMain.performGetMethod(webClient) +{{#media-json}} .thenAccept(it -> assertThat(it, is("{\"message\":\"Hello World!\"}"))) +{{/media-json}} +{{^media-json}} + .thenAccept(it -> assertThat(it, is("Hello World!"))) +{{/media-json}} .await(); } diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 465906f898d..c779978f2c7 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -886,6 +886,11 @@ hibernate-validator-cdi ${version.lib.hibernate-validator} + + org.hibernate.validator + hibernate-validator + ${version.lib.hibernate-validator} + org.jboss.narayana.jta narayana-jta-jakarta diff --git a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml index 76d7ca05368..a6ecd25955a 100644 --- a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml +++ b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml @@ -41,8 +41,8 @@ 2.7.5.1 1.6.0 3.0.0-M5 - 3.0.0-M3 - 3.0.0-M3 + 3.0.0-RC3 + 3.0.0-RC3 1.0.6 3.0.2 1.5.0.Final diff --git a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml index ea7012bb3fb..3968a2d5d6f 100644 --- a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml +++ b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml @@ -40,8 +40,8 @@ 3.0.0 1.6.0 3.0.0-M5 - 3.0.0-M3 - 3.0.0-M3 + 3.0.0-RC3 + 3.0.0-RC3 3.0.2 1.5.0.Final 0.5.1 diff --git a/pom.xml b/pom.xml index e93c5a898c6..4af78727d36 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 1.6.0 3.0.0-M5 2.3 - 3.0.0-RC2 + 3.0.0-RC3 ${version.lib.hibernate} 0.8.5 1.1.0 @@ -123,7 +123,7 @@ 1.4 2.14.0 - 3.0.0-RC2 + 3.0.0-RC3 https://jakarta.ee/specifications/restful-ws/3.0/apidocs/