diff --git a/docs/src/main/asciidoc/includes/cors.adoc b/docs/src/main/asciidoc/includes/cors.adoc index b1433b8b810..48e7800225a 100644 --- a/docs/src/main/asciidoc/includes/cors.adoc +++ b/docs/src/main/asciidoc/includes/cors.adoc @@ -106,14 +106,14 @@ ifdef::se-flavor[] [source,yaml] ---- restrictive-cors: - allow-origins: ["http://foo.com", "http://there.com"] + allow-origins: ["https://foo.com", "https://there.com"] allow-methods: ["PUT", "DELETE"] ---- endif::[] ifndef::se-flavor[] [source,yaml] ---- -allow-origins: ["http://foo.com", "http://there.com"] +allow-origins: ["https://foo.com", "https://there.com"] allow-methods: ["PUT", "DELETE"] ---- endif::[] @@ -207,15 +207,15 @@ You can use mapped configuration to your advantage if you want to specify all CO The following example illustrates the mapped cross-origin configuration format. -[source,hocon,subs="attributes+"] +[source,yaml,subs="attributes+"] ---- -{mapped-config-top-key}: // <1> +{mapped-config-top-key}: # <1> paths: <2> - path-pattern: /greeting // <3> - allow-origins: ["http://foo.com", "http://there.com", "http://other.com"] // <4> + allow-origins: ["https://foo.com", "https://there.com", "https://other.com"] # <4> allow-methods: ["PUT", "DELETE"] - - path-pattern: / // <5> - allow-methods: ["GET", "HEAD", "OPTIONS", "POST"] // <6> + - path-pattern: / # <5> + allow-methods: ["GET", "HEAD", "OPTIONS", "POST"] # <6> ---- // We want to use the following to insert the SE or MP callout 1 text; we need to use the blank, plus, // and subs because the MP attribute value contains backticks, and this is the only way we've found @@ -337,15 +337,15 @@ CORS configuration section. // Tag the following example so we can exclude it from MP which supplies its own complete example. // tag::se-config-example[] The following example restricts sharing of the -`{built-in-service-prefix}/health` resource, provided by the health built-in service, to only the origin `\http://there.com`. +`{built-in-service-prefix}/health` resource, provided by the health built-in service, to only the origin `\https://there.com`. [source,hocon,subs="attributes+"] ---- cors: paths: - path-pattern: "{built-in-service-prefix}/health" - allow-origins: [http://there.com] + allow-origins: [https://there.com] - path-pattern: "{built-in-service-prefix}/metrics" - allow-origins: [http://foo.com] + allow-origins: [https://foo.com] ---- // end::se-config-example[] @@ -368,10 +368,11 @@ Build and run the QuickStart application as usual. The metrics service rejects attempts to access metrics on behalf of a disallowed origin. [source,bash,subs="attributes+"] ---- -curl -i -H "Origin: http://other.com" http://localhost:8080{built-in-service-prefix}/metrics +curl -i -H "Origin: https://other.com" http://localhost:8080{built-in-service-prefix}/metrics ---- [source, listing] +.Curl output ---- HTTP/1.1 403 Forbidden Date: Mon, 11 May 2020 11:08:09 -0500 @@ -382,13 +383,14 @@ connection: keep-alive But accesses from `foo.com` succeed. [source,bash,subs="attributes+"] ---- -curl -i -H "Origin: http://foo.com" http://localhost:8080{built-in-service-prefix}/metrics +curl -i -H "Origin: https://foo.com" http://localhost:8080{built-in-service-prefix}/metrics ---- [source, listing] +.Curl output ---- HTTP/1.1 200 OK -Access-Control-Allow-Origin: http://foo.com +Access-Control-Allow-Origin: https://foo.com Content-Type: text/plain Date: Mon, 11 May 2020 11:08:16 -0500 Vary: Origin @@ -405,7 +407,7 @@ The health service rejects requests from origins not specifically approved. [source,bash,subs="attributes+"] ---- -curl -i -H "Origin: http://foo.com" http://localhost:8080{built-in-service-prefix}/health +curl -i -H "Origin: https://foo.com" http://localhost:8080{built-in-service-prefix}/health ---- [source, listing] @@ -416,17 +418,17 @@ transfer-encoding: chunked connection: keep-alive ---- -And responds successfully only to cross-origin requests from `\http://there.com`. +And responds successfully only to cross-origin requests from `\https://there.com`. [source,bash,subs="attributes+"] ---- -curl -i -H "Origin: http://there.com" http://localhost:8080{built-in-service-prefix}/health +curl -i -H "Origin: https://there.com" http://localhost:8080{built-in-service-prefix}/health ---- [source, listing] ---- HTTP/1.1 200 OK -Access-Control-Allow-Origin: http://there.com +Access-Control-Allow-Origin: https://there.com Content-Type: application/json Date: Mon, 11 May 2020 12:07:32 -0500 Vary: Origin diff --git a/docs/src/main/asciidoc/includes/metrics/metrics-shared.adoc b/docs/src/main/asciidoc/includes/metrics/metrics-shared.adoc index 66301fca95b..c3ce8bde3be 100644 --- a/docs/src/main/asciidoc/includes/metrics/metrics-shared.adoc +++ b/docs/src/main/asciidoc/includes/metrics/metrics-shared.adoc @@ -119,26 +119,26 @@ Further, clients can narrow down to a specific metric name by adding the name as curl -s -H 'Accept: text/plain' -X GET http://localhost:8080{metrics-endpoint} ---- -[listing] +[source,text] ---- # 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,subs="attributes+"] +.Example Reporting: JSON format ---- curl -s -H 'Accept: application/json' -X GET http://localhost:8080{metrics-endpoint} ---- -[listing] +[source,json] +.JSON response: ---- { "base" : { "memory.maxHeap" : 3817865216, - "memory.committedHeap" : 335544320, + "memory.committedHeap" : 335544320 } } ---- diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-ui.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-ui.adoc index 918a0d19372..588d78aed77 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-ui.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-ui.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2022, 2023 Oracle and/or its affiliates. + Copyright (c) 2022, 2024 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ for the Helidon MP QuickStart greeting application. endif::mp-flavor[] .OpenAPI UI Screen for Helidon {flavor-uc} QuickStart Greeting Application -image::{screen-capture-start}[align="center",title="Example OpenAPI UI Screen"] +image::{screen-capture-start}[align="center",title="Example OpenAPI UI Screen",role="fit"] // end::usage-start[] @@ -129,7 +129,7 @@ The next image shows the screen after you submit the "Returns a personalized gre Note that the UI shows the actual response from invoking the operation in the "Server response" section. The "Responses" section farther below describes the possible responses from the operation as declared in the OpenAPI document for the application. .Example OpenAPI UI Screen -image::{screen-capture-expanded}[align="center",title="Example OpenAPI UI Screen"] +image::{screen-capture-expanded}[align="center",title="Example OpenAPI UI Screen",role="fit"] // end::usage-expanded-screen[] // end::usage[] diff --git a/docs/src/main/asciidoc/includes/openapi/openapi.adoc b/docs/src/main/asciidoc/includes/openapi/openapi.adoc index 44f6daeffff..4e345fc3a5c 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi.adoc @@ -56,11 +56,11 @@ endif::[] // end::overview[] // tag::mp-depc[] - - io.helidon.microprofile.openapi - helidon-microprofile-openapi - runtime - + + io.helidon.microprofile.openapi + helidon-microprofile-openapi + runtime + // end::mp-depc[] // tag::furnish-openapi-info[] diff --git a/docs/src/main/asciidoc/includes/security/providers/oidc.adoc b/docs/src/main/asciidoc/includes/security/providers/oidc.adoc index 8a647a2a05b..078a0f761d1 100644 --- a/docs/src/main/asciidoc/includes/security/providers/oidc.adoc +++ b/docs/src/main/asciidoc/includes/security/providers/oidc.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2023 Oracle and/or its affiliates. + Copyright (c) 2018, 2024 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,11 +36,11 @@ security: - oidc: client-id: "client-id-of-this-service" client-secret: "${CLEAR=client-secret-of-this-service}" - identity-uri: "http://your-tenant.identity-server.com" + identity-uri: "https://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-origins: ["https://foo.com", "https://there.com"] allow-methods: ["PUT", "DELETE"] outbound: - name: "internal-services" @@ -94,7 +94,7 @@ the `tenant-id-style` configuration option. For more information, see the table ---- tenants: - name: "example-tenant" - ... tenant configuration options + # ... tenant configuration options ---- There are four ways to provide the required tenant information to Helidon by default. @@ -141,7 +141,7 @@ and the custom tenant configuration discovery can be provided by implementing SP include::{rootdir}/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc[leveloffset=+3,tag=config] ==== How does that work? -Multitenant support requires to obtain tenant name from the incoming request. OIDC configuration is selected +Multi-tenant support requires to obtain tenant name from the incoming request. OIDC configuration is selected based on the received tenant name. The way this tenant name has to be provided is configured via `tenant-id-style` configuration. See <> for more information. After matching tenant configuration with the received name, the rest of the OIDC flow if exactly the same as in <>. diff --git a/docs/src/main/asciidoc/mp/config/advanced-configuration.adoc b/docs/src/main/asciidoc/mp/config/advanced-configuration.adoc index 21553940fe4..88138114009 100644 --- a/docs/src/main/asciidoc/mp/config/advanced-configuration.adoc +++ b/docs/src/main/asciidoc/mp/config/advanced-configuration.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, 2023 Oracle and/or its affiliates. + Copyright (c) 2020, 2024 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -62,14 +62,7 @@ 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> +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_1, indent=0] ---- <1> Creates MicroProfile Config Source builder. <2> Adds environment variables. @@ -86,9 +79,7 @@ it with the builder. [source,java] .Create YamlMPConfigSource from a path ---- -ConfigProviderResolver.instance().newBuilder() - .withSources(YamlMpConfigSource.create(path)) - .build(); +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_2, indent=0] ---- == Creating Custom Config Sources @@ -96,7 +87,7 @@ ConfigProviderResolver.instance().newBuilder() Custom Config Sources are loaded using the Java Service Loader pattern, by implementing either `org.eclipse.microprofile.config.spi.ConfigSource`, or `org.eclipse.microprofile.config.spi.ConfigSourceProvider` SPI and registering -it as a service (Using `META-INF/services/${class-name}` file when using classpath, or +it as a service (Using `pass:[META-INF/services/${class-name}]` file when using classpath, or using the `provides` statement in `module-info.java` when using module path). The interface `org.eclipse.microprofile.config.spi.ConfigSource` requires implementation of the following methods: @@ -110,34 +101,8 @@ The interface `org.eclipse.microprofile.config.spi.ConfigSource` requires implem [source,java] ---- -public class CustomConfigSource implements ConfigSource { - private static final String NAME = "MyConfigSource"; - private static final int ORDINAL = 200; // Default for MP is 100 - private static final Map PROPERTIES = mapOf("app.greeting", "Hi"); - - - @Override - public String getName() { - return NAME; <1> - } - - @Override - public Map getProperties() { - return PROPERTIES; <2> - } - - @Override - public String getValue(String key) { - return PROPERTIES.get(key); <3> - } - - @Override - public int getOrdinal() { - return ORDINAL; <4> - } -} +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_3, indent=0] ---- - <1> Returns the name of the Config Source to use for logging or analysis of configured values. <2> Returns the properties in this Config Source as a map. <3> Returns the value of the requested key, or `null` if the key is not available @@ -159,26 +124,24 @@ or environment variable `HELIDON_MP_META_CONFIG` [source,yaml] .Example of a YAML meta configuration file: ---- -add-discovered-sources: true <1> -add-discovered-converters: false <2> -add-default-sources: false <3> +add-discovered-sources: true # <1> +add-discovered-converters: false # <2> +add-default-sources: false # <3> sources: - - type: "environment-variables" <4> - - type: "system-properties" <5> - - type: "properties" <6> - path: "/conf/prod.properties" <7> - ordinal: 50 <8> - 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> - + - type: "environment-variables" # <4> + - type: "system-properties" # <5> + - type: "properties" # <6> + path: "/conf/prod.properties" # <7> + ordinal: 50 # <8> + 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> ---- - <1> If configured to `true`, config sources discovered through service loader will be added <2> If configured to `true`, converters discovered through service loader will be added <3> If configured to `true`, default config sources (system properties, environment variables, and `META-INF/microprofile-config.properties) will be added @@ -199,15 +162,15 @@ sources: [source,xml] ---- - - io.helidon.config - helidon-config-hocon-mp - + + 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). +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 `pass:[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: @@ -218,54 +181,8 @@ The interface `io.helidon.config.mp.spi.MpMetaConfigProvider` requires implement [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> - -} +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_4, indent=0] ---- - <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` @@ -280,11 +197,9 @@ public class CustomMpMetaConfigProvider implements MpMetaConfigProvider { To use the Helidon SE features in Helidon MP, create MicroProfile Config Source from Helidon SE Config Source. The Config Source is immutable regardless of configured polling strategy or change watchers. +[source,java] ---- -Config config = ConfigProviderResolver.instance() - .getBuilder() - .withSources(MpConfigSources.create(helidonConfigSource) <1> - .build(); +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_5, indent=0] ---- <1> Creates a MicroProfile config instance using Helidon Config Source. @@ -293,17 +208,10 @@ Config config = ConfigProviderResolver.instance() To use advanced Helidon SE features in Helidon MP, create MicroProfile Config Source from Helidon SE Config. The Config Source is mutable if the config uses either polling strategy and change watchers, or polling strategy or change watchers. The latest config version is queried each time `org.eclipse.microprofile.config.spi.ConfigSource#getValue(String)` is called. +[source,java] ---- -io.helidon.config.Config helidonConfig = io.helidon.config.Config.builder() - .addSource(ConfigSources.create(Map.of("key", "value"))) <1> - .build(); -ConfigProviderResolver.instance(); -Config config = ConfigProviderResolver.instance() - .getBuilder() - .withSources(MpConfigSources.create(helidonConfig)) <2> - .build(); +include::{sourcedir}/mp/config/AdvancedConfigurationSnippets.java[tag=snippet_6, indent=0] ---- - <1> Creates a config source from Helidon Config. <2> Creates a MicroProfile config instance using Helidon Config. diff --git a/docs/src/main/asciidoc/mp/config/introduction.adoc b/docs/src/main/asciidoc/mp/config/introduction.adoc index e2c94c993d3..e3b1cf3b727 100644 --- a/docs/src/main/asciidoc/mp/config/introduction.adoc +++ b/docs/src/main/asciidoc/mp/config/introduction.adoc @@ -81,17 +81,13 @@ instance programmatically or injecting configuration values with `@ConfigPropert [source,java] .Using `Config` ---- -org.eclipse.microprofile.config.Config config = ConfigProvider.getConfig(); -config.getOptionalValue("app.greeting", String.class).orElse("Hello"); +include::{sourcedir}/mp/config/IntroductionSnippets.java[tag=snippet_1, indent=0] ---- [source,java] .Injecting configured properties into a constructor ---- -@Inject -public GreetingProvider(@ConfigProperty(name = "app.greeting", defaultValue = "Hello") String message) { - this.message = message -} +include::{sourcedir}/mp/config/IntroductionSnippets.java[tag=snippet_2, indent=0] ---- {spec-name} provides typed access to configuration values, using built-in converters, and `Converter` implementations located by Java Service Loader. @@ -116,10 +112,10 @@ The example below shows how the MicroProfile configuration file `microprofile-co [source,properties] ---- -// Application properties. This is the default greeting +# Application properties. This is the default greeting app.greeting=Hello -// Microprofile server properties +# Microprofile server properties server.port=8080 server.host=0.0.0.0 ---- @@ -202,10 +198,10 @@ For more information, see xref:{rootdir}/mp/security/configuration-secrets.adoc[ # Password encrypted using a master password client_secret=${GCM=mYRkg+4Q4hua1kvpCCI2hg==} # Password encrypted using public key (there are length limits when using RSA) -client_secret=${RSA=mYRkg+4Q4hua1kvpCCI2hg==} +client_secret_pke=${RSA=mYRkg+4Q4hua1kvpCCI2hg==} # Password in clear text, can be used in development # The system needs to be configured to accept clear text -client_secret=${CLEAR=known_password} +client_secret_clear=${CLEAR=known_password} ---- ==== Meta Configuration diff --git a/docs/src/main/asciidoc/mp/cors/cors.adoc b/docs/src/main/asciidoc/mp/cors/cors.adoc index 40f76540bb2..8acc4094b8b 100644 --- a/docs/src/main/asciidoc/mp/cors/cors.adoc +++ b/docs/src/main/asciidoc/mp/cors/cors.adoc @@ -74,8 +74,8 @@ Adding CORS behavior to your Helidon MP application involves just a few simple s For each resource class in your application: -. Identify the resources and subresources--in other words, the paths--declared in the resource class which you want to support CORS. -. For each of those resources and subresources which should support CORS: +. Identify the resources and sub-resources--in other words, the paths--declared in the resource class which you want to support CORS. +. For each of those resources and sub-resources which should support CORS: .. Find or create a Java method annotated with `@OPTIONS` and with the correct `@Path`. .. To that `@OPTIONS` Java method add a Helidon link:{mp-cors-javadoc-base-url}/io/helidon/microprofile/cors/CrossOrigin.html[`@CrossOrigin`] annotation that describes the cross-origin sharing you want for that resource. @@ -103,7 +103,7 @@ For each resource you want to configure, add a section to `META-INF/microprofile [source,properties,subs="verbatim,quotes"] ---- cors.enabled= # <1> -# + # <2> cors.paths._i_.path-pattern= # <3> cors.paths._i_.allow-headers= @@ -116,7 +116,7 @@ cors.paths._i_.enabled= # <4> ---- <1> You can disable CORS processing for all resources by setting `cors.enabled` to `false`. Defaults to `true`. <2> Add a block for each resource you want to configure. -The index `_i_` is an integer (0, 1, 2, etc.). +The index `_i_` is an integer (0, 1, 2, etc). <3> Specify the settings as needed to define the CORS behavior you want for that resource. <4> The `enabled` setting lets you control whether the system uses that set of CORS configuration. Defaults to `true`. @@ -169,7 +169,7 @@ You could use the following configuration in place of using annotations to set u cors.paths.0.path-pattern=/greet # <1> cors.paths.1.path-pattern=/greet/greeting # <2> -cors.paths.1.allow-origins=http://foo.com,http://there.com +cors.paths.1.allow-origins=https://foo.com,https://there.com cors.paths.1.allow-methods=PUT ---- <1> Enables default CORS settings for the `/greet` resource. @@ -182,11 +182,11 @@ Or, alternatively, the following configuration example augments the settings fro ---- cors.paths.0.path-pattern=/greet # <1> cors.paths.0.allow-methods=GET -cors.paths.0.allow-origins=http://here.com,http://foo.com,http://there.com +cors.paths.0.allow-origins=https://here.com,https://foo.com,https://there.com -cors.paths.1.path-patterh=/greet/greeting # <2> +cors.paths.1.path-pattern=/greet/greeting # <2> cors.paths.1.allow-methods=PUT -cors.paths.1.allow-origins=http://foo.com +cors.paths.1.allow-origins=https://foo.com ---- <1> Changes the declared settings to restrict cross-origin use of `/greet` to only `GET` and only from `foo.com` and `there.com`. <2> Changes the settings for `/greet/greeting` from what they were declared; with this configuration, only the origin `foo.com` is permitted. (The declared setting also allowed `there.com`). @@ -208,16 +208,16 @@ include::{rootdir}/includes/cors.adoc[tags=configuring-cors-for-builtin-services The following example restricts sharing of -* the `/health` resource, provided by the health built-in service, to only the origin `\http://there.com`, and -* the `/metrics` resource, provided by the metrics built-in service, to only the origin `\http://foo.com`. +* the `/health` resource, provided by the health built-in service, to only the origin `\https://there.com`, and +* the `/metrics` resource, provided by the metrics built-in service, to only the origin `\https://foo.com`. .Configuration which restricts sharing of the health and metrics resources [source,properties] ---- cors.paths.0.path-pattern=/health -cors.paths.0.allow-origins=http://there.com +cors.paths.0.allow-origins=https://there.com cors.paths.1.path-pattern=/metrics -cors.paths.1.allow-origins=http://foo.com +cors.paths.1.allow-origins=https://foo.com ---- include::{rootdir}/includes/cors.adoc[tag=accessing-shared-resources-intro] @@ -228,6 +228,7 @@ java -jar target/helidon-quickstart-mp.jar ---- [listing,bash] +.Console output ---- ... 2020.05.12 05:44:08 INFO io.helidon.microprofile.server.ServerCdiExtension Thread[main,5,main]: Server started on http://localhost:8080 (and all other host addresses) in 5280 milliseconds (since JVM startup). diff --git a/docs/src/main/asciidoc/mp/guides/config.adoc b/docs/src/main/asciidoc/mp/guides/config.adoc index 589c23e6fad..afc13a2627d 100644 --- a/docs/src/main/asciidoc/mp/guides/config.adoc +++ b/docs/src/main/asciidoc/mp/guides/config.adoc @@ -176,12 +176,13 @@ java -jar target/helidon-quickstart-mp.jar ---- [source,bash] -.Invoke the endpoint below and check the response: +.Invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, hocon] +.JSON response: ---- { "message": "HelloFromEnvironment World!" // <1> @@ -200,12 +201,13 @@ java -Dapp.greeting="HelloFromSystemProperty" -jar target/helidon-quickstart-mp ---- [source,bash] -.Invoke the endpoint below and check the response: +.Invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, hocon] +.JSON response: ---- { "message": "HelloFromSystemProperty World!" // <1> @@ -255,12 +257,13 @@ include::{sourcedir}/mp/guides/ConfigSnippets.java[tag=snippet_4, indent=0] <2> Define a class member variable to hold the greeting. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, json] +.JSON response: ---- { "message": "HelloFromMPConfig World!" @@ -280,12 +283,13 @@ include::{sourcedir}/mp/guides/ConfigSnippets.java[tag=snippet_5, indent=0] <2> Get the `app.greeting` value from the `Config` object and set the member variable. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, json] +.JSON response: ---- { "message": "HelloFromMPConfig World!" @@ -331,12 +335,13 @@ include::{sourcedir}/mp/guides/ConfigSnippets.java[tag=snippet_6, indent=0] <3> Get the value from the `sender` `Config` node. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, json] +.JSON response: ---- { "message": "Joe says Hello-from-config-file.yaml World!" @@ -364,12 +369,13 @@ include::{sourcedir}/mp/guides/ConfigSnippets.java[tag=snippet_8, indent=0] ---- [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint ---- curl http://localhost:8080/greet ---- [source, json] +.JSON response: ---- { "message": "HelloFromConfigFile World!" @@ -405,7 +411,6 @@ kind: ConfigMap <1> The file `config-file.properties` will be created within the Kubernetes container. <2> The `config-file.properties` file will have this single property defined. - [source,yaml] .Create the Kubernetes YAML specification, named `k8s-config.yaml`, with the following contents: ---- @@ -487,6 +492,7 @@ curl http://localhost:31143/greet ---- [source, hocon] +.JSON response: ---- { "message": "HelloFromConfigFile World!" // <1> diff --git a/docs/src/main/asciidoc/mp/guides/health.adoc b/docs/src/main/asciidoc/mp/guides/health.adoc index b1d6f7d09c3..62e26e727c2 100644 --- a/docs/src/main/asciidoc/mp/guides/health.adoc +++ b/docs/src/main/asciidoc/mp/guides/health.adoc @@ -130,7 +130,7 @@ include::{sourcedir}/mp/guides/HealthSnippets.java[tag=snippet_1, indent=0] <3> Build the HealthCheckResponse with status `UP` and the current time. [source,bash] -.Build and run the application, then verify the custom liveness health endpoint: +.Build and run the application, then verify the custom liveness health endpoint ---- curl http://localhost:8080/health/live ---- diff --git a/docs/src/main/asciidoc/mp/guides/metrics.adoc b/docs/src/main/asciidoc/mp/guides/metrics.adoc index d466d630634..48d8593ef68 100644 --- a/docs/src/main/asciidoc/mp/guides/metrics.adoc +++ b/docs/src/main/asciidoc/mp/guides/metrics.adoc @@ -229,7 +229,7 @@ curl -H "Accept: application/json" 'http://localhost:8080{metrics-endpoint}?sco ---- [source,hocon] -.JSON response (partial)`: +.JSON response (partial): ---- { "anyCard": 1, diff --git a/docs/src/main/asciidoc/mp/guides/mp-tutorial.adoc b/docs/src/main/asciidoc/mp/guides/mp-tutorial.adoc index 518589bf8c8..e0b50df56fe 100644 --- a/docs/src/main/asciidoc/mp/guides/mp-tutorial.adoc +++ b/docs/src/main/asciidoc/mp/guides/mp-tutorial.adoc @@ -258,13 +258,13 @@ At this stage, the application is a very simple "Hello World" greeting service. For example: [source,bash] -.Try the Application +.Try the Application: ---- curl -X GET http://localhost:7001/greet ---- [source, json] -.JSON response +.JSON response: ---- {"message":"Hello World!"} ---- diff --git a/docs/src/main/asciidoc/mp/guides/security-oidc.adoc b/docs/src/main/asciidoc/mp/guides/security-oidc.adoc index 7c2743f2431..8fc28ce7c83 100644 --- a/docs/src/main/asciidoc/mp/guides/security-oidc.adoc +++ b/docs/src/main/asciidoc/mp/guides/security-oidc.adoc @@ -36,8 +36,9 @@ include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] == Introduction -This guide describes the steps required to protect your whole application or a specific area with Open ID Connect -(OIDC) security. OIDC is a secure mechanism for an application to contact an identity service. +This guide describes the steps required to protect your whole application or a specific area with Open ID Connect (OIDC) security. + +OIDC is a secure mechanism for an application to contact an identity service. It's built on top of OAuth 2.0 and provides full-fledged authentication and authorization protocols. == Install Keycloak @@ -478,10 +479,12 @@ In the body of the request, select `x-www-form-urlencoded` type. Add the followi [source,json] .Enter the key:value ---- -[{"key":"grant_type","value":"authorization_code"}, -{"key":"client_id","value":"myClientID"}, -{"key":"client_secret","value":"client secret"}, -{"key":"code","value":"authorization code"}] +[ + {"key":"grant_type","value":"authorization_code"}, + {"key":"client_id","value":"myClientID"}, + {"key":"client_secret","value":"client secret"}, + {"key":"code","value":"authorization code"} +] ---- Do not forget to replace the `client secret` by its value (generated during Create a Client), and `authorization code` @@ -498,15 +501,17 @@ sentence `Add authorization data to` with `Request Headers`, and complete the r [source,json] .Enter the following information: ---- -[{"key":"Header Prefix","value":"bearer"}, -{"key":"Grant type","value":"Password Credentials"}, -{"key":"Access Token URL","value":"http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/token"}, -{"key":"Client ID","value":"myClientID"}, -{"key":"Client Secret","value":"client secret"}, -{"key":"Username","value":"myuser"}, -{"key":"Password","value":"password"}, -{"key":"Scope","value":"openid"}, -{"key":"Client Authentication","value":"Send as Basic Auth Header"}] +[ + {"key":"Header Prefix","value":"bearer"}, + {"key":"Grant type","value":"Password Credentials"}, + {"key":"Access Token URL","value":"http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/token"}, + {"key":"Client ID","value":"myClientID"}, + {"key":"Client Secret","value":"client secret"}, + {"key":"Username","value":"myuser"}, + {"key":"Password","value":"password"}, + {"key":"Scope","value":"openid"}, + {"key":"Client Authentication","value":"Send as Basic Auth Header"} +] ---- Again, make sure to replace `client secret` by the actual client secret. Click on `Get New Access Token`. A popup diff --git a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc index c2673fb744c..1a3bec3b795 100644 --- a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc +++ b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc @@ -86,24 +86,7 @@ The test is only useful if it invokes the server and verifies the result. To sup [source,java] .Updated Class with webTarget ---- -import static org.junit.jupiter.api.Assertions.assertEquals; - -@HelidonTest -class GreetTest { - @Inject - WebTarget webTarget; - - @Test - void testDefaultGreeting() { - JsonObject jsonObject = webTarget.path("/greet") - .request() - .get(JsonObject.class); - - String expected = "Hello World!"; - String actual = jsonObject.getString("message"); - assertEquals(expected, actual, "Message in JSON"); - } -} +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_2, indent=0] ---- The test is now complete and verifies the message. @@ -128,7 +111,7 @@ Here's an example showing how these approaches are used to execute the same endp [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_2, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_3, indent=0] ---- === Use Beans for Testing @@ -137,14 +120,14 @@ If you prefer to use only beans for testing, and want to add a different bean fo [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_3, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_4, indent=0] ---- By default, the bean is added to the container with scope set to `ApplicationScoped`. You can customize scope either by annotating the bean class with another scope or through the annotation: [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_4, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_5, indent=0] ---- @@ -158,7 +141,7 @@ For this purpose, we provide the following annotation which adds the extension t [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_5, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_6, indent=0] ---- @@ -168,7 +151,7 @@ If you want to disable discovery and only add custom extensions and beans, then [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_6, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_7, indent=0] ---- NOTE: This annotation is typically used in conjunction with `@AddBeans` and/or `@AddExtension`. As you have seen in standard test output, by default Helidon starts with the dependencies defined in pom.xml. @@ -180,7 +163,7 @@ If you want just the basic test features enabled, then you only have to add a fe [source,java] ---- -include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_7, indent=0] +include::{sourcedir}/mp/guides/TestingJunit5Snippets.java[tag=snippet_8, indent=0] ---- == Summary diff --git a/docs/src/main/asciidoc/mp/guides/tracing.adoc b/docs/src/main/asciidoc/mp/guides/tracing.adoc index e830d5a22e1..9286cf01877 100644 --- a/docs/src/main/asciidoc/mp/guides/tracing.adoc +++ b/docs/src/main/asciidoc/mp/guides/tracing.adoc @@ -159,6 +159,7 @@ curl http://localhost:8080/greet ---- [source, json] +.JSON response: ---- { "message": "Hello World!" @@ -172,17 +173,17 @@ The tracing output data is verbose and can be difficult to interpret using the R Click on the UI Find traces button (the search icon) as shown in the image below. .Jaeger UI -image::guides/12_tracing_refresh.png[Trace Refresh] +image::guides/12_tracing_refresh.png[Trace Refresh,role="fit"] The image below shows the trace summary, including start time and duration of each trace. There are several traces, each one generated in response to a `curl http://localhost:8080/greet` invocation. The oldest trace will have a much longer duration since there is one-time initialization that occurs. .Tracing list view -image::guides/12_tracing_top.png[Traces] +image::guides/12_tracing_top.png[Traces,role="fit"] Click on a trace, and you will see the trace detail page where the spans are listed. You can clearly see the root span and the relationship among all the spans in the trace, along with timing information. .Trace detail page -image::guides/12_tracing_detail.png[Trace Detail] +image::guides/12_tracing_detail.png[Trace Detail,role="fit"] NOTE: A parent span might not depend on the result of the child. This is called a `FollowsFrom` reference, see https://github.com/opentracing/specification/blob/master/specification.md[Open Tracing Semantic Spec]. Note that the last span that writes the response after the root span ends falls into this category. @@ -314,6 +315,7 @@ curl http://localhost:8081/greet ---- [source, json] +.JSON response: ---- { "message": "Hello From MP-2 World!" @@ -352,7 +354,7 @@ curl -i http://localhost:8080/greet/outbound # <1> Refresh the Jaeger UI trace listing page and notice that there is a trace across two services. .Tracing across multiple services detail view -image::guides/12_tracing_detail_2_services.png[Traces] +image::guides/12_tracing_detail_2_services.png[Traces,role="fit"] In the image above, you can see that the trace includes spans from two services. You will notice there is a gap before the sixth span, which is a `get` operation. This is a one-time client initialization delay. Run the `/outbound` curl command again and look at the new trace to see that the delay no longer exists. @@ -496,6 +498,7 @@ curl http://localhost:31143/greet ---- [source, json] +.JSON response: ---- { "message": "Hello World!" diff --git a/docs/src/main/asciidoc/mp/health.adoc b/docs/src/main/asciidoc/mp/health.adoc index 76e3858ebd5..1fe64f20518 100644 --- a/docs/src/main/asciidoc/mp/health.adoc +++ b/docs/src/main/asciidoc/mp/health.adoc @@ -305,7 +305,7 @@ curl -v http://localhost:8080/health/ready <1> The HTTP status is `503` since the application is not ready. [source,json] -.JSON response body: +.JSON response: ---- { "status": "DOWN", @@ -335,7 +335,7 @@ curl -v http://localhost:8080/health/ready <1> The HTTP status is `200` indicating that the application is ready. [source,json] -.JSON response body: +.JSON response: ---- { "status": "UP", @@ -383,7 +383,7 @@ curl -v http://localhost:8080/health/started [source,json] -.JSON response body: +.JSON response: ---- { "status": "DOWN", @@ -413,7 +413,7 @@ curl -v http://localhost:8080/health/started <1> The HTTP status is `200` indicating that the application is started. [source,json] -.JSON response body: +.JSON response: ---- { "status": "UP", diff --git a/docs/src/main/asciidoc/mp/integrations/neo4j.adoc b/docs/src/main/asciidoc/mp/integrations/neo4j.adoc index 8a24de4d71a..bba247dfd30 100644 --- a/docs/src/main/asciidoc/mp/integrations/neo4j.adoc +++ b/docs/src/main/asciidoc/mp/integrations/neo4j.adoc @@ -60,8 +60,8 @@ First describe Neo4j connection properties: # Neo4j settings neo4j.uri=bolt://localhost:7687 neo4j.authentication.username=neo4j -neo4j.authentication.password: secret -neo4j.pool.metricsEnabled: true +neo4j.authentication.password=secret +neo4j.pool.metricsEnabled=true ---- Then just inject the driver: @@ -75,7 +75,7 @@ The driver can be used according to the link:https://neo4j.com/developer/java/[N == Configuration -/include::{rootdir}/config/io_helidon_integrations_neo4j_Neo4j.adoc[leveloffset=+1,tag=config] +include::{rootdir}/config/io_helidon_integrations_neo4j_Neo4j.adoc[leveloffset=+1,tag=config] == Examples @@ -117,8 +117,8 @@ Next add the connection configuration properties for Neo4j: # Neo4j settings neo4j.uri=bolt://localhost:7687 neo4j.authentication.username=neo4j -neo4j.authentication.password: secret -neo4j.pool.metricsEnabled: true +neo4j.authentication.password=secret +neo4j.pool.metricsEnabled=true # Enable the optional MicroProfile Metrics REST.request metrics metrics.rest-request.enabled=true diff --git a/docs/src/main/asciidoc/mp/jaxrs/helidon-connector.adoc b/docs/src/main/asciidoc/mp/jaxrs/helidon-connector.adoc index 4a347cfe09c..cab34bcd99f 100644 --- a/docs/src/main/asciidoc/mp/jaxrs/helidon-connector.adoc +++ b/docs/src/main/asciidoc/mp/jaxrs/helidon-connector.adoc @@ -77,8 +77,8 @@ turn off redirects, you can add the following lines to your `microprofile-config [source, properties] ---- - jersey.connector.helidon.config.cookie-manager.automatic-store-enabled=true - jersey.connector.helidon.config.follow-redirects=false +jersey.connector.helidon.config.cookie-manager.automatic-store-enabled=true +jersey.connector.helidon.config.follow-redirects=false ---- Alternatively, assuming the root of the `WebClient` configuration is located at diff --git a/docs/src/main/asciidoc/mp/jwt.adoc b/docs/src/main/asciidoc/mp/jwt.adoc index 1f8c8dd6ee9..9bd621144c7 100644 --- a/docs/src/main/asciidoc/mp/jwt.adoc +++ b/docs/src/main/asciidoc/mp/jwt.adoc @@ -120,12 +120,10 @@ 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] +.Curl output ---- HTTP/1.1 200 OK Date: 08.06.2022 10:33:47 EEST diff --git a/docs/src/main/asciidoc/mp/lra.adoc b/docs/src/main/asciidoc/mp/lra.adoc index 2d80db76583..b1cdddd6138 100644 --- a/docs/src/main/asciidoc/mp/lra.adoc +++ b/docs/src/main/asciidoc/mp/lra.adoc @@ -382,9 +382,9 @@ 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 +curl 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 \ +-o narayana-coordinator.jar +java -Dquarkus.http.port=8070 -jar narayana-coordinator.jar ---- Narayana LRA coordinator is running by default under `lra-coordinator` context, diff --git a/docs/src/main/asciidoc/mp/metrics/metrics.adoc b/docs/src/main/asciidoc/mp/metrics/metrics.adoc index 1273496dd56..c0de7d88eaf 100644 --- a/docs/src/main/asciidoc/mp/metrics/metrics.adoc +++ b/docs/src/main/asciidoc/mp/metrics/metrics.adoc @@ -162,7 +162,7 @@ java -jar target/helidon-quickstart-mp.jar // tag::access-cards-example[] .Access the application endpoints -[source,base] +[source,bash] ---- curl http://localhost:8080/cards curl http://localhost:8080/cards diff --git a/docs/src/main/asciidoc/mp/reactivemessaging/weblogic.adoc b/docs/src/main/asciidoc/mp/reactivemessaging/weblogic.adoc index f4e3e811f3f..2cf089981e1 100644 --- a/docs/src/main/asciidoc/mp/reactivemessaging/weblogic.adoc +++ b/docs/src/main/asciidoc/mp/reactivemessaging/weblogic.adoc @@ -192,7 +192,7 @@ Helidon application needs to be aware about our WLS SSL public certificate. .Running example with WLS truststore ---- java --add-opens=java.base/java.io=ALL-UNNAMED \ --Djavax.net.ssl.trustStore=DemoTrust.jks \ --Djavax.net.ssl.trustStorePassword=DemoTrustKeyStorePassPhrase \ --jar ./target/helidon-wls-jms-demo.jar + -Djavax.net.ssl.trustStore=DemoTrust.jks \ + -Djavax.net.ssl.trustStorePassword=DemoTrustKeyStorePassPhrase \ + -jar ./target/helidon-wls-jms-demo.jar ---- diff --git a/docs/src/main/asciidoc/mp/server.adoc b/docs/src/main/asciidoc/mp/server.adoc index e94d93a4da1..dfe5087ad86 100644 --- a/docs/src/main/asciidoc/mp/server.adoc +++ b/docs/src/main/asciidoc/mp/server.adoc @@ -357,6 +357,7 @@ io.helidon.examples.AdminApplication: path: "/management" ---- +[[_using_requested_uri_discovery]] === Using Requested URI Discovery include::{requested-uri-discovery-inc}[tag=intro] diff --git a/docs/src/main/asciidoc/mp/telemetry.adoc b/docs/src/main/asciidoc/mp/telemetry.adoc index 67aa68e9a36..e2896e50a4b 100644 --- a/docs/src/main/asciidoc/mp/telemetry.adoc +++ b/docs/src/main/asciidoc/mp/telemetry.adoc @@ -58,7 +58,7 @@ In a distributed tracing system, *traces* are used to capture a series of reques Finally, *exporters* are responsible for transmitting the collected trace data to a backend service for monitoring and visualization. This enables developers to gain a comprehensive understanding of the system's behavior and detect any issues or bottlenecks that may arise. -image::telemetry/telemetry-general.png[General understanding of OpenTelemetry Tracing] +image::telemetry/telemetry-general.png[General understanding of OpenTelemetry Tracing,role="fit"] There are two ways to work with Telemetry, using: @@ -210,14 +210,16 @@ Together with Helidon Telemetry dependency, an OpenTelemetry Exporter dependency [source,xml] ---- - - io.helidon.microprofile.telemetry - helidon-microprofile-telemetry <1> - - - io.opentelemetry - opentelemetry-exporter-jaeger <2> - + + + io.helidon.microprofile.telemetry + helidon-microprofile-telemetry + + + io.opentelemetry + opentelemetry-exporter-jaeger + + ---- <1> Helidon Telemetry dependency. <2> OpenTelemetry Jaeger exporter. @@ -270,14 +272,14 @@ Hello World Next, launch the Jaeger UI at link:http://localhost:16686/[]. The expected output is: -image::telemetry/telemetry-greeting-jaeger.png[Greeting service tracing output] +image::telemetry/telemetry-greeting-jaeger.png[Greeting service tracing output,role="fit"] .Custom method [source,java] ---- include::{sourcedir}/mp/TelemetrySnippets.java[tag=snippet_8, indent=0] ---- -<1> Inject Opentelemetry `Tracer`. +<1> Inject OpenTelemetry `Tracer`. <2> Create a span around the method `useCustomSpan()`. <3> Create a custom `INTERNAL` span and start it. <4> End the custom span. @@ -291,7 +293,7 @@ curl localhost:8080/greeting/custom Again you can launch the Jaeger UI at link:http://localhost:16686/[]. The expected output is: -image::telemetry/telemetry-custom-jaeger.png[Custom span usage] +image::telemetry/telemetry-custom-jaeger.png[Custom span usage,role="fit"] Now let us use multiple services calls. In the example below our main service will call the `secondary` services. Each method in each service will be annotated with `@WithSpan` annotation. @@ -326,7 +328,7 @@ The `greeting-service` call `secondary-service`. Each service will create spans Launch the Jaeger UI at link:http://localhost:16686/[] to see the expected output (shown below). -image::telemetry/telemetry-outbound-jaeger.png[Secondary service outbound call] +image::telemetry/telemetry-outbound-jaeger.png[Secondary service outbound call,role="fit"] This example is available at the link:{helidon-github-tree-url}/examples/microprofile/telemetry[Helidon official GitHub repository]. diff --git a/docs/src/main/asciidoc/mp/tracing.adoc b/docs/src/main/asciidoc/mp/tracing.adoc index 077c8d863d2..60db9cfe0e3 100644 --- a/docs/src/main/asciidoc/mp/tracing.adoc +++ b/docs/src/main/asciidoc/mp/tracing.adoc @@ -295,7 +295,7 @@ curl -i http://localhost:8080/greet/outbound # <1> Refresh the Jaeger UI trace listing page and notice that there is a trace across two services. .Tracing across multiple services detail view -image::guides/12_tracing_detail_2_services.png[Traces] +image::guides/12_tracing_detail_2_services.png[Traces,role="fit"] In the image above, you can see that the trace includes spans from two services. You will notice there is a gap before the sixth span, which is a `get` operation. This is a one-time client initialization delay. Run the `/outbound` curl command again and look at the new trace to see that the delay no longer exists. diff --git a/docs/src/main/asciidoc/se/config/advanced-configuration.adoc b/docs/src/main/asciidoc/se/config/advanced-configuration.adoc index 9d337567ad2..79a5d88f54a 100644 --- a/docs/src/main/asciidoc/se/config/advanced-configuration.adoc +++ b/docs/src/main/asciidoc/se/config/advanced-configuration.adoc @@ -413,7 +413,6 @@ names `oracle` and `oracle.com`. "secured" : true } } - ---- [source,java] diff --git a/docs/src/main/asciidoc/se/config/hierarchical-features.adoc b/docs/src/main/asciidoc/se/config/hierarchical-features.adoc index ddea29089af..3b01c089a10 100644 --- a/docs/src/main/asciidoc/se/config/hierarchical-features.adoc +++ b/docs/src/main/asciidoc/se/config/hierarchical-features.adoc @@ -228,7 +228,6 @@ or `MISSING` ---- include::{sourcedir}/se/config/HierarchicalFeaturesSnippets.java[tag=snippet_3, indent=0] ---- - <1> Get the ConfigValue with child `Config` instances. <2> Map the node list to names using the Java Stream API (if present) <3> Use an empty list if the "app" node does not exist @@ -239,7 +238,6 @@ include::{sourcedir}/se/config/HierarchicalFeaturesSnippets.java[tag=snippet_3, ---- include::{sourcedir}/se/config/HierarchicalFeaturesSnippets.java[tag=snippet_4, indent=0] ---- - <1> Get child nodes of the `data.providers` _list_ node as a `List` of `Config` instances. <2> Check that the list contains the expected child nodes with keys `data.providers.0` and `data.providers.1`. @@ -254,19 +252,18 @@ Depending on the structure of the loaded configuration the stream contains a mix ---- include::{sourcedir}/se/config/HierarchicalFeaturesSnippets.java[tag=snippet_5, indent=0] ---- - <1> Visit the subtree rooted at the `data.providers` _list_ node. <2> Prints out following list of nodes (type and key): -==== -[listing] +[source,text] +---- OBJECT data.providers.0 VALUE data.providers.0.name VALUE data.providers.0.class OBJECT data.providers.1 VALUE data.providers.1.name VALUE data.providers.1.class -==== +---- The optional `Predicate` argument to the `traverse` methods allows the application to prune the traversal of a subtree at any point. @@ -276,20 +273,19 @@ application to prune the traversal of a subtree at any point. ---- include::{sourcedir}/se/config/HierarchicalFeaturesSnippets.java[tag=snippet_6, indent=0] ---- - <1> Visit all _root_ sub-nodes, excluding whole `data` tree structure but including others. <2> Prints out following list of nodes (type and key): -==== -[listing] +[source,listing] +---- OBJECT app VALUE app.page-size VALUE app.greeting LIST app.basic-range VALUE app.basic-range.0 VALUE app.basic-range.1 -==== +---- == Detaching a Config Subtree Sometimes it can be convenient to write part of your application to deal with diff --git a/docs/src/main/asciidoc/se/config/introduction.adoc b/docs/src/main/asciidoc/se/config/introduction.adoc index 5eea052f8c5..3d434f7a722 100644 --- a/docs/src/main/asciidoc/se/config/introduction.adoc +++ b/docs/src/main/asciidoc/se/config/introduction.adoc @@ -236,6 +236,7 @@ See xref:advanced-configuration.adoc#filters-and-overrides[Filter, Overrides, an The `Config` object lets your application retrieve config data as a typed ConfigValue. You can retrieve a `ConfigValue` using the following `as` methods in `Config`: + * `asString()` - to get a string config value * `asBoolean()` and other accessors for primitive types * `as(Class)` - to get a value for a type that has a mapper configured @@ -245,6 +246,7 @@ You can retrieve a `ConfigValue` using the following `as` methods in `Config` * `as(Function)` - to get a typed value providing a mapper function ConfigValue can be used to obtain: + * an `Optional` value _from a single node_, * the `T` value _from a single node_ interpreted as a basic Java type (primitive or simple object) already known to the config system (such as a `boolean` or a `Double`), or * a complex Java type _from a subtree_ of the config tree. diff --git a/docs/src/main/asciidoc/se/cors.adoc b/docs/src/main/asciidoc/se/cors.adoc index 46956fdac8a..bf57443827e 100644 --- a/docs/src/main/asciidoc/se/cors.adoc +++ b/docs/src/main/asciidoc/se/cors.adoc @@ -181,7 +181,7 @@ To use this automatic support, make sure your configuration contains a `cors` se cors: paths: - path-pattern: /greeting - allow-origins: ["http://foo.com", "http://there.com", "http://other.com"] + allow-origins: ["https://foo.com", "https://there.com", "https://other.com"] allow-methods: ["PUT", "DELETE"] - path-pattern: / allow-methods: ["GET", "HEAD", "OPTIONS", "POST"] diff --git a/docs/src/main/asciidoc/se/guides/config.adoc b/docs/src/main/asciidoc/se/guides/config.adoc index 524ecdee73d..5289f00f63b 100644 --- a/docs/src/main/asciidoc/se/guides/config.adoc +++ b/docs/src/main/asciidoc/se/guides/config.adoc @@ -245,13 +245,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_2, indent=0] even though it is considered a default source. [source,bash] -.Build and run the application (without the system property). Invoke the endpoint and check the response: +.Build and run the application (without the system property). Invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFrom-config.properties World!" // <1> @@ -273,13 +273,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_3, indent=0] <1> Swap the source order, putting `application.yaml` first. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFrom-application.yaml World!" // <1> @@ -313,13 +313,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_4, indent=0] <1> Add a mandatory configuration file. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFrom-config-file.properties World!" // <1> @@ -376,7 +376,7 @@ curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFromFileInDirectoryConf World!" // <1> @@ -396,13 +396,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_8, indent=0] <1> Add each config source using the `addSource` method. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFromFileInDirectoryConf World!" @@ -440,13 +440,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_9, indent=0] <1> Will use `config-profile.yaml` by default [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFrom-application.yaml World!" // <1> @@ -477,13 +477,13 @@ sources: <3> Specify that the `optional-config-file` file is optional. [source,bash] -.Restart the application, then invoke the endpoint below and check the response: +.Restart the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "HelloFrom-config-file.properties World!" // <1> @@ -547,13 +547,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_11, indent=0] <1> Get the `app` node, then get the child node, `greeting`. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "HelloFrom-application.yaml World!" @@ -597,13 +597,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_12, indent=0] <3> Add the `greeting` node to the collection. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "HelloFrom-application.yaml under child2a World!" @@ -643,13 +643,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_13, indent=0] update the greeting with the new value. [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "HelloFrom-config-file.properties World!" @@ -670,7 +670,7 @@ curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "Updated HelloFrom-config-file.properties World!" // <1> @@ -707,13 +707,13 @@ include::{sourcedir}/se/guides/ConfigSnippets.java[tag=snippet_15, indent=0] ---- [source,bash] -.Build and run the application, then invoke the endpoint and check the response: +.Build and run the application, then invoke the endpoint: ---- curl http://localhost:8080/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "Hello World!" // <1> @@ -834,7 +834,7 @@ curl http://localhost:31143/greet ---- [source,hocon] -.JSON response +.JSON response: ---- { "message": "Updated HelloFrom-config-file.properties World!" // <1> diff --git a/docs/src/main/asciidoc/se/guides/health.adoc b/docs/src/main/asciidoc/se/guides/health.adoc index fa1908ea898..f2295f1a2a3 100644 --- a/docs/src/main/asciidoc/se/guides/health.adoc +++ b/docs/src/main/asciidoc/se/guides/health.adoc @@ -96,7 +96,7 @@ curl -v http://localhost:8080/observe/health ---- The verbose `curl` output reports the HTTP status: -[listing] +[source,text] ---- < HTTP/1.1 204 No Content ---- diff --git a/docs/src/main/asciidoc/se/guides/tracing.adoc b/docs/src/main/asciidoc/se/guides/tracing.adoc index 8212d198098..94f1ec24de8 100644 --- a/docs/src/main/asciidoc/se/guides/tracing.adoc +++ b/docs/src/main/asciidoc/se/guides/tracing.adoc @@ -230,7 +230,7 @@ curl http://localhost:8080/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "Hello World!" @@ -347,13 +347,13 @@ java -jar target/helidon-quickstart-se-2.jar ---- [source,bash] -.Run the curl command in a new terminal window and check the response (*notice the port is 8081*) : +.Run the curl command in a new terminal window (*notice the port is 8081*) : ---- curl http://localhost:8081/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "Hello From SE-2 World!" @@ -431,7 +431,7 @@ curl -i http://localhost:8080/greet/outbound # <1> <1> The request goes to the service on `8080`, which then invokes the service at `8081` to get the greeting. [source,hocon] -.JSON response +.JSON response: ---- { "message": "Hello From SE-2 World!" // <1> @@ -594,7 +594,7 @@ curl http://localhost:31143/greet ---- [source,json] -.JSON response +.JSON response: ---- { "message": "Hello World!" diff --git a/docs/src/main/asciidoc/se/health.adoc b/docs/src/main/asciidoc/se/health.adoc index e391de477c2..b999f91a5b9 100644 --- a/docs/src/main/asciidoc/se/health.adoc +++ b/docs/src/main/asciidoc/se/health.adoc @@ -442,7 +442,7 @@ Accessing the Helidon-provided `/observe/health` endpoint reports the health of as shown below: [source,json] -.JSON response. +.JSON response: ---- { "status": "UP", diff --git a/docs/src/main/asciidoc/se/integrations/hcv.adoc b/docs/src/main/asciidoc/se/integrations/hcv.adoc index f23588776b4..394c645ef64 100644 --- a/docs/src/main/asciidoc/se/integrations/hcv.adoc +++ b/docs/src/main/asciidoc/se/integrations/hcv.adoc @@ -194,7 +194,7 @@ Cubbyhole secrets engine operations: [source,java] ---- -include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_4, indent=0] +include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_5, indent=0] ---- <1> Create a secret from request entity. @@ -206,7 +206,7 @@ Key/Value version 1 secrets engine operations: [source,java] ---- -include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_5, indent=0] +include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_6, indent=0] ---- <1> Disable the secrets engine on the default path. @@ -221,7 +221,7 @@ Key/Value version 2 secrets engine operations: [source,java] ---- -include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_6, indent=0] +include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_7, indent=0] ---- <1> Create a secret from request entity. @@ -234,7 +234,7 @@ Transit secrets engine operations: [source,java] ---- -include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_7, indent=0] +include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_8, indent=0] ---- <1> Enable the secrets engine on the default path. @@ -254,7 +254,7 @@ In order to use Kubernetes authentication: [source,java] ---- -include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_8, indent=0] +include::{sourcedir}/se/integrations/HcvSnippets.java[tag=snippet_9, indent=0] ---- <1> Run the Kubernetes Authentication by enabling it. diff --git a/docs/src/main/asciidoc/se/metrics/metrics.adoc b/docs/src/main/asciidoc/se/metrics/metrics.adoc index a6165b28bbd..9c8de809c1e 100644 --- a/docs/src/main/asciidoc/se/metrics/metrics.adoc +++ b/docs/src/main/asciidoc/se/metrics/metrics.adoc @@ -198,8 +198,6 @@ The following example, based on the Helidon SE QuickStart application, shows how [source,java] ---- include::{sourcedir}/se/metrics/MetricsSnippets.java[tag=snippet_2, indent=0] - -include::{sourcedir}/se/metrics/MetricsSnippets.java[tag=snippet_3, indent=0] ---- <1> Get the global meter registry. <2> Create (or find) a counter named "accessctr" in the global registry. @@ -219,7 +217,11 @@ java -jar target/helidon-quickstart-se.jar [source, bash] ---- curl 'http://localhost:8080/observe/metrics?scope=application' # <1> +---- +[source,text] +.Response +---- # HELP accessctr_total # TYPE accessctr_total counter accessctr_total{scope="application",} 0.0 # <2> @@ -231,7 +233,11 @@ accessctr_total{scope="application",} 0.0 # <2> [source,bash] ---- curl http://localhost:8080/greet +---- +[source,json] +.JSON response: +---- {"message":"Hello World"} ---- @@ -239,7 +245,11 @@ curl http://localhost:8080/greet [source,bash] ---- curl 'http://localhost:8080/observe/metrics?scope=application' +---- +[source,text] +.Response +---- # HELP accessctr_total # TYPE accessctr_total counter accessctr_total{scope="application",} 1.0 # <1> @@ -288,9 +298,7 @@ Your code creates a link:{prometheus-javadoc-base-url}/io/helidon/metrics/promet [source,java] ---- -include::{sourcedir}/se/metrics/MetricsSnippets.java[tag=snippet_4, indent=0] - -include::{sourcedir}/se/metrics/MetricsSnippets.java[tag=snippet_5, indent=0] +include::{sourcedir}/se/metrics/MetricsSnippets.java[tag=snippet_3, indent=0] ---- This example uses the default Prometheus `CollectorRegistry`. By default, the `PrometheusSupport` and exposes its REST endpoint at the path diff --git a/docs/src/main/asciidoc/se/testing.adoc b/docs/src/main/asciidoc/se/testing.adoc index b6bb8b15648..a1cdeef75e2 100644 --- a/docs/src/main/asciidoc/se/testing.adoc +++ b/docs/src/main/asciidoc/se/testing.adoc @@ -230,7 +230,7 @@ The WebSocket Testing extension adds support for routing configuration and injec .WebSocket sample test. [source,java] ---- -include::{sourcedir}/se/TestingSnippets.java[tag=snippet_4, indent=0] +include::{sourcedir}/se/TestingSnippets.java[tag=snippet_3, indent=0] ---- <1> Declare `WsClient` and later inject it in the constructor. <2> Using @SetUpRoute, create WebSocket routing and assign a serverside listener. diff --git a/docs/src/main/asciidoc/se/tracing.adoc b/docs/src/main/asciidoc/se/tracing.adoc index 10bbb144c09..692ff4c3c77 100644 --- a/docs/src/main/asciidoc/se/tracing.adoc +++ b/docs/src/main/asciidoc/se/tracing.adoc @@ -49,14 +49,16 @@ include::{rootdir}/includes/dependencies.adoc[] // tag::tracing-dependency[] [source,xml] ---- - - io.helidon.tracing - helidon-tracing - - - io.helidon.webserver.observe - helidon-webserver-observe-tracing - + + + io.helidon.tracing + helidon-tracing + + + io.helidon.webserver.observe + helidon-webserver-observe-tracing + + ---- <1> Helidon tracing dependency. <2> Observability dependencies for tracing. @@ -268,14 +270,16 @@ Tracing propagation is automatic as long as the current span context is availabl [source,xml] ---- - - io.helidon.webclient - helidon-webclient - - - io.helidon.webclient - helidon-webclient-tracing - + + + io.helidon.webclient + helidon-webclient + + + io.helidon.webclient + helidon-webclient-tracing + + ---- [source,java] diff --git a/docs/src/main/java/io/helidon/docs/includes/reactivestreams/EngineSnippets.java b/docs/src/main/java/io/helidon/docs/includes/reactivestreams/EngineSnippets.java index 6c8edda41ac..df4441291fd 100644 --- a/docs/src/main/java/io/helidon/docs/includes/reactivestreams/EngineSnippets.java +++ b/docs/src/main/java/io/helidon/docs/includes/reactivestreams/EngineSnippets.java @@ -69,7 +69,7 @@ void snippet_3() { // > Item received: FOO // > Item received: BAR + // end::snippet_3[] } - // end::snippet_3[] } diff --git a/docs/src/main/java/io/helidon/docs/mp/TelemetrySnippets.java b/docs/src/main/java/io/helidon/docs/mp/TelemetrySnippets.java index 6209e678c6a..f7bb37a4472 100644 --- a/docs/src/main/java/io/helidon/docs/mp/TelemetrySnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/TelemetrySnippets.java @@ -47,14 +47,12 @@ static class GreetingMessage { @ApplicationScoped class HelidonBean { - @WithSpan - // <1> + @WithSpan // <1> void doSomethingWithinSpan() { // do something here } - @WithSpan("name") - // <2> + @WithSpan("name") // <2> void complexSpan(@SpanAttribute(value = "arg") String arg) { // do something here } diff --git a/docs/src/main/java/io/helidon/docs/mp/config/AdvancedConfigurationSnippets.java b/docs/src/main/java/io/helidon/docs/mp/config/AdvancedConfigurationSnippets.java new file mode 100644 index 00000000000..6cf7cfb42db --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/config/AdvancedConfigurationSnippets.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.docs.mp.config; + +import java.net.URL; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import io.helidon.config.ConfigException; +import io.helidon.config.ConfigSources; +import io.helidon.config.ConfigValue; +import io.helidon.config.mp.MpConfigSources; +import io.helidon.config.mp.spi.MpMetaConfigProvider; +import io.helidon.config.yaml.mp.YamlMpConfigSource; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.ConfigSource; + +@SuppressWarnings("ALL") +class AdvancedConfigurationSnippets { + + void snippet_1() { + // tag::snippet_1[] + ConfigProviderResolver resolver = ConfigProviderResolver.instance(); + + Config config = resolver.getBuilder() // <1> + .withSources(MpConfigSources.environmentVariables()) // <2> + .withSources(MpConfigSources.create(Map.of("key", "value"))) // <3> + .build(); // <4> + + resolver.registerConfig(config, null); // <5> + // end::snippet_1[] + } + + void snippet_2(Path path) { + // tag::snippet_2[] + ConfigProviderResolver.instance().getBuilder() + .withSources(YamlMpConfigSource.create(path)) + .build(); + // end::snippet_2[] + } + + class Snippet3 { + // tag::snippet_3[] + public class CustomConfigSource implements ConfigSource { + private static final String NAME = "MyConfigSource"; + private static final int ORDINAL = 200; // Default for MP is 100 + private static final Map PROPERTIES = Map.of("app.greeting", "Hi"); + + @Override + public String getName() { + return NAME; // <1> + } + + @Override + public Map getProperties() { + return PROPERTIES; // <2> + } + + @Override + public Set getPropertyNames() { + return PROPERTIES.keySet(); + } + + @Override + public String getValue(String key) { + return PROPERTIES.get(key); // <3> + } + + @Override + public int getOrdinal() { + return ORDINAL; // <4> + } + } + // end::snippet_3[] + } + + // stub + static List sourceFromPath(Path path, String profile) { + return List.of(); + } + + // stub + static List sourceFromClasspath(String classpath, String profile) { + return List.of(); + } + + // stub + static List sourceFromUrlMeta(URL url, String profile) { + return List.of(); + } + + // tag::snippet_4[] + public class CustomMpMetaConfigProvider implements MpMetaConfigProvider { + + @Override + public Set supportedTypes() { + return Set.of("custom"); // <1> + } + + @Override + public List create(String type, io.helidon.config.Config metaConfig, String profile) { + ConfigValue pathConfig = metaConfig.get("path").as(Path.class); + String location; + if (pathConfig.isPresent()) { // <2> + Path path = pathConfig.get(); + List sources = sourceFromPath(path, profile); // <3> + if (sources != null && !sources.isEmpty()) { + return sources; + } + 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 " + metaConfig.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> + } + } + // end::snippet_4[] + + void snippet_5(io.helidon.config.Config helidonConfigSource) { + // tag::snippet_5[] + Config config = ConfigProviderResolver.instance() + .getBuilder() + .withSources(MpConfigSources.create(helidonConfigSource)) // <1> + .build(); + // end::snippet_5[] + } + + void snippet_6() { + // tag::snippet_6[] + io.helidon.config.Config helidonConfig = io.helidon.config.Config.builder() + .addSource(ConfigSources.create(Map.of("key", "value"))) // <1> + .build(); + ConfigProviderResolver.instance(); + Config config = ConfigProviderResolver.instance() + .getBuilder() + .withSources(MpConfigSources.create(helidonConfig)) // <2> + .build(); + // end::snippet_6[] + } + +} diff --git a/docs/src/main/java/io/helidon/docs/mp/config/IntroductionSnippets.java b/docs/src/main/java/io/helidon/docs/mp/config/IntroductionSnippets.java new file mode 100644 index 00000000000..e156c7d997e --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/config/IntroductionSnippets.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.docs.mp.config; + +import jakarta.inject.Inject; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@SuppressWarnings("ALL") +class IntroductionSnippets { + + void snippet_1() { + // tag::snippet_1[] + Config config = ConfigProvider.getConfig(); + config.getOptionalValue("app.greeting", String.class).orElse("Hello"); + // end::snippet_1[] + } + + class Snippet2 { + + class GreetingProvider { + + String message; + + // tag::snippet_2[] + @Inject + public GreetingProvider( + @ConfigProperty(name = "app.greeting", + defaultValue = "Hello") String message) { + this.message = message; + } + // end::snippet_2[] + } + } + +} diff --git a/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java b/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java index 87cb38c16d5..155f53cbe0e 100644 --- a/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java @@ -59,6 +59,26 @@ void testDefaultGreeting() { class Snippet2 { // tag::snippet_2[] + @HelidonTest + class GreetTest { + @Inject + WebTarget webTarget; + + @Test + void testDefaultGreeting() { + JsonObject jsonObject = webTarget.path("/greet") + .request() + .get(JsonObject.class); + + assertThat(jsonObject.getString("message"), is("Hello World!")); + } + } + // end::snippet_2[] + } + + class Snippet3 { + + // tag::snippet_3[] @HelidonTest(resetPerTest = true) class GreetTest { @Test @@ -84,48 +104,48 @@ private void validate(WebTarget webTarget, assertThat(message, is("Message in JSON")); } } - // end::snippet_2[] + // end::snippet_3[] } class Snippet4 { - // tag::snippet_3[] + // tag::snippet_4[] @AddBean(TestBean.class) class GreetTest { } - // end::snippet_3[] + // end::snippet_4[] } class Snippet5 { - // tag::snippet_4[] + // tag::snippet_5[] @AddBean(value = TestBean.class, scope = Dependent.class) class GreetTest { } - // end::snippet_4[] + // end::snippet_5[] } class Snippet6 { - // tag::snippet_5[] + // tag::snippet_6[] @AddExtension(TestExtension.class) class GreetTest { } - // end::snippet_5[] + // end::snippet_6[] } class Snippet7 { - // tag::snippet_6[] + // tag::snippet_7[] @DisableDiscovery class GreetTest { } - // end::snippet_6[] + // end::snippet_7[] } class Snippet8 { - // tag::snippet_7[] + // tag::snippet_8[] @HelidonTest @DisableDiscovery @AddExtension(ConfigCdiExtension.class) @@ -150,7 +170,7 @@ String message() { } } } - // end::snippet_7[] + // end::snippet_8[] } } diff --git a/docs/src/main/java/io/helidon/docs/mp/metrics/MetricsSnippets.java b/docs/src/main/java/io/helidon/docs/mp/metrics/MetricsSnippets.java index e8103efb855..12a5582d329 100644 --- a/docs/src/main/java/io/helidon/docs/mp/metrics/MetricsSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/metrics/MetricsSnippets.java @@ -90,10 +90,9 @@ private JsonObject createResponse(String msg) { return JSON.createObjectBuilder().add("message", msg).build(); } } + // end::snippet_2[] } - // end::snippet_2[] - class Snippet3 { // tag::snippet_3[] diff --git a/docs/src/main/java/io/helidon/docs/se/TestingSnippets.java b/docs/src/main/java/io/helidon/docs/se/TestingSnippets.java index 0a84fe18bc3..a3c9eef2bfb 100644 --- a/docs/src/main/java/io/helidon/docs/se/TestingSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/TestingSnippets.java @@ -97,17 +97,30 @@ void testRootRoute() { // <4> } // end::snippet_2[] - // tag::snippet_5[] - static class ServerSideListener implements WsListener { - volatile String message; + // tag::snippet_3[] + @ServerTest + class WsSocketTest { - @Override - public void onMessage(WsSession session, String text, boolean last) { // <1> - message = text; - session.send("ws", true); + static final ServerSideListener WS_LISTENER = new ServerSideListener(); + final WsClient wsClient; // <1> + + WsSocketTest(WsClient wsClient) { + this.wsClient = wsClient; + } + + @SetUpRoute + static void routing(WsRouting.Builder ws) { // <2> + ws.endpoint("/testWs", WS_LISTENER); + } + + @Test + void testWsEndpoint() { // <3> + ClientSideListener clientListener = new ClientSideListener(); + wsClient.connect("/testWs", clientListener); // <4> + assertThat(clientListener.message, is("ws")); // <5> } } - // end::snippet_5[] + // end::snippet_3[] // tag::snippet_4[] static class ClientSideListener implements WsListener { @@ -132,28 +145,15 @@ public void onError(WsSession session, Throwable t) { // <3> } // end::snippet_4[] - // tag::snippet_4[] - @ServerTest - class WsSocketTest { - - static final ServerSideListener WS_LISTENER = new ServerSideListener(); - final WsClient wsClient; // <1> - - WsSocketTest(WsClient wsClient) { - this.wsClient = wsClient; - } - - @SetUpRoute - static void routing(WsRouting.Builder ws) { // <2> - ws.endpoint("/testWs", WS_LISTENER); - } + // tag::snippet_5[] + static class ServerSideListener implements WsListener { + volatile String message; - @Test - void testWsEndpoint() { // <3> - ClientSideListener clientListener = new ClientSideListener(); - wsClient.connect("/testWs", clientListener); // <4> - assertThat(clientListener.message, is("ws")); // <5> + @Override + public void onMessage(WsSession session, String text, boolean last) { // <1> + message = text; + session.send("ws", true); } } - // end::snippet_4[] + // end::snippet_5[] } diff --git a/docs/src/main/java/io/helidon/docs/se/config/MutabilitySupportSnippets.java b/docs/src/main/java/io/helidon/docs/se/config/MutabilitySupportSnippets.java index 40a7cfa5778..3e72b78b1ad 100644 --- a/docs/src/main/java/io/helidon/docs/se/config/MutabilitySupportSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/config/MutabilitySupportSnippets.java @@ -33,8 +33,8 @@ void snippet_1(Config myConfig) { // end::snippet_1[] } - // tag::snippet_2[] void snippet_2() { + // tag::snippet_2[] Config config = Config.create( ConfigSources.file("conf/dev.properties") .pollingStrategy(PollingStrategies.regular(Duration.ofSeconds(2))) // <1> @@ -47,8 +47,8 @@ void snippet_2() { // end::snippet_2[] } - // tag::snippet_3[] void snippet_3(Config config) { + // tag::snippet_3[] config.get("greeting") // <1> .onChange((changedNode) -> { // <2> System.out.println("Node " + changedNode.key() + " has changed!"); diff --git a/docs/src/main/java/io/helidon/docs/se/integrations/HcvSnippets.java b/docs/src/main/java/io/helidon/docs/se/integrations/HcvSnippets.java index 9e525b3a0ea..f241498a40f 100644 --- a/docs/src/main/java/io/helidon/docs/se/integrations/HcvSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/integrations/HcvSnippets.java @@ -50,6 +50,11 @@ @SuppressWarnings("ALL") class HcvSnippets { + // stub + final class VaultPolicy { + static final String POLICY = ""; + } + void kv2Secrets(Config config) { // tag::snippet_1[] Vault vault = Vault.builder() @@ -93,7 +98,7 @@ void webserverExample(Vault tokenVault, Config config) { record CubbyholeService(Sys sys, CubbyholeSecrets secrets) implements HttpService { - // tag::snippet_4[] + // tag::snippet_5[] @Override public void routing(HttpRules rules) { rules.get("/create", this::createSecrets) @@ -116,12 +121,12 @@ void getSecret(ServerRequest req, ServerResponse res) { // <2> res.send(); } } - // end::snippet_4[] + // end::snippet_5[] } record Kv1Service(Sys sys, Kv1Secrets secrets) implements HttpService { - // tag::snippet_5[] + // tag::snippet_6[] @Override public void routing(HttpRules rules) { rules.get("/enable", this::enableEngine) @@ -164,12 +169,12 @@ void getSecret(ServerRequest req, ServerResponse res) { // <5> res.send(); } } - // end::snippet_5[] + // end::snippet_6[] } record Kv2Service(Sys sys, Kv2Secrets secrets) implements HttpService { - // tag::snippet_6[] + // tag::snippet_7[] @Override public void routing(HttpRules rules) { rules.get("/create", this::createSecrets) @@ -201,7 +206,7 @@ void getSecret(ServerRequest req, ServerResponse res) { // <3> res.send(); } } - // end::snippet_6[] + // end::snippet_7[] } record TransitService(Sys sys, TransitSecrets secrets) implements HttpService { @@ -210,7 +215,7 @@ record TransitService(Sys sys, TransitSecrets secrets) implements HttpService { static final String SIGNATURE_KEY = ""; static final Base64Value SECRET_STRING = Base64Value.create(""); - // tag::snippet_7[] + // tag::snippet_8[] @Override public void routing(HttpRules rules) { rules.get("/enable", this::enableEngine) @@ -316,18 +321,14 @@ void verify(ServerRequest req, ServerResponse res) { // <10> res.send("Valid: " + verifyResponse.isValid()); } - // end::snippet_7[] + // end::snippet_8[] void batch(ServerRequest req, ServerResponse res) { // stub } } - final class VaultPolicy { - static final String POLICY = ""; - } - - // tag::snippet_8[] + // tag::snippet_9[] class K8sExample { private static final String SECRET_PATH = "k8s/example/secret"; private static final String POLICY_NAME = "k8s_policy"; @@ -397,6 +398,6 @@ private void enableK8sAuth() { // <4> k8sVault = Vault.create(config); } } - // end::snippet_8[] + // end::snippet_9[] } diff --git a/docs/src/main/java/io/helidon/docs/se/metrics/MetricsSnippets.java b/docs/src/main/java/io/helidon/docs/se/metrics/MetricsSnippets.java index ae5a1785836..894c3d05c49 100644 --- a/docs/src/main/java/io/helidon/docs/se/metrics/MetricsSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/metrics/MetricsSnippets.java @@ -16,10 +16,8 @@ package io.helidon.docs.se.metrics; import io.helidon.config.Config; -// tag::snippet_2[] import io.helidon.metrics.api.Counter; import io.helidon.metrics.api.Metrics; -// end::snippet_2[] // tag::snippet_4[] import io.helidon.metrics.prometheus.PrometheusSupport; // end::snippet_4[] @@ -66,7 +64,7 @@ void snippet_1(Config config) { // end::snippet_1[] } - // tag::snippet_3[] + // tag::snippet_2[] public class GreetService implements HttpService { private final Counter accessCtr = Metrics.globalRegistry() // <1> @@ -104,13 +102,13 @@ void updateGreetingHandler(ServerRequest request, // ... } } - // end::snippet_3[] + // end::snippet_2[] void snippet_5(HttpRouting.Builder routing) { - // tag::snippet_5[] + // tag::snippet_3[] routing .addFeature(PrometheusSupport.create()) .register("/myapp", new MyService()); - // end::snippet_5[] + // end::snippet_3[] } }