diff --git a/docs/about/additional_info.adoc b/docs/about/additional_info.adoc index e81d146e678..136c9f87d17 100644 --- a/docs/about/additional_info.adoc +++ b/docs/about/additional_info.adoc @@ -16,5 +16,21 @@ /////////////////////////////////////////////////////////////////////////////// -= Learn More About Helidon += Helidon Community and Support References +:description: Helidon community links and references +:keywords: helidon, java, microservices, microprofile, documentation, nima +:rootdir: {docdir}/.. + +include::{rootdir}/includes/attributes.adoc[] + +Here are some additional resources you can use to get started, get help, and to follow the evolution of Helidon. + + +* Project website: https://helidon.io +* Medium publication: https://medium.com/helidon +* GitHub repository: https://github.com/helidon-io/helidon +* Public Slack workspace: https://slack.helidon.io/ +* Twitter account: https://twitter.com/helidon_project +* YouTube channel: https://www.youtube.com/channel/UChg00-uTTrCMmPsuzUNaZsA[https://www.youtube.com/channel/UChg00-uTTrCMmPsuzUNaZsA] +* Stack Overflow tag: https://stackoverflow.com/tags/helidon diff --git a/docs/about/doc_overview.adoc b/docs/about/doc_overview.adoc index 5ce044d23fc..7f7ad999bc9 100644 --- a/docs/about/doc_overview.adoc +++ b/docs/about/doc_overview.adoc @@ -16,14 +16,16 @@ /////////////////////////////////////////////////////////////////////////////// -= Documentation Overview += Helidon Documentation :description: Helidon -:keywords: helidon, java, microservices, microprofile +:keywords: helidon, java, microservices, microprofile, documentation, nima :rootdir: {docdir}/.. +Check out the xref:{rootdir}/about/doc_sitemap.adoc[Documentation Overview] for information about this site and the materials we provide. + include::{rootdir}/includes/attributes.adoc[] -This is test to see if this displays. Can we add some text about what's included in the docs? + [PILLARS] ==== @@ -40,7 +42,7 @@ xref:{rootdir}/about/benefits.adoc[Features and Benefits] xref:{rootdir}/about/archetype.adoc[Helidon Archetypes] -xref:{rootdir}/about/additional_info.adoc[Reference and Community Resources] +xref:{rootdir}/about/additional_info.adoc[Helidon Community and Support] xref:{rootdir}/about/intro.adoc[What's New in Helidon 4] @@ -49,9 +51,9 @@ xref:{rootdir}/about/intro.adoc[What's New in Helidon 4] //Getting Started [CARD] .Getting Started -[icon=settings] +[icon=rocket_launch] -- -There are several ways to generate your first Helidon project. In a hurry? The Helidon CLI and Project Starter provide a set of predefined archetypes to choose from. Or try the Quickstart guides for the standard Helidon Maven archetypes. +There are several ways to generate your first Helidon project. In a hurry? Try the Helidon CLI or Project Starter which provide a set of predefined archetypes to choose from. xref:{rootdir}/about/prerequisites.adoc[Prerequisites and System Requirements] @@ -88,7 +90,7 @@ xref:{rootdir}/se/guides/migration.adoc[Upgrade From Helidon SE 2.x] //Developing Your First Application [CARD] .Developing Your First Application From Scratch -[icon=share] +[icon=terminal] -- Use these hands-on development tutorials to learn how to generate a small "Hello World!" greeting service that demonstrates some of Helidon's core features. @@ -101,27 +103,27 @@ xref:{rootdir}/mp/guides/mp-tutorial.adoc[Helidon SE Tutorial] //How-To Guides [CARD] .How-To Guides -[icon=graphic_eq] +[icon=library_books] -- Ready for more hands-on learning? Explore the features of Helidon with these quick step-by-step guides. -Nima Guides +xref:{rootdir}/nima/guides/overview.adoc[Níma Guides] xref:{rootdir}/se/guides/overview.adoc[SE Guides] xref:{rootdir}/mp/guides/overview.adoc[MP Guides] -- -//Helidon Nima +//Advanced Features [CARD] -.Helidon Nima +.Advanced Features [icon=swap_horiz] -- -- -//Helidon MP +//Training and Certification [CARD] .Helidon MP [icon=swap_horiz] @@ -129,9 +131,9 @@ xref:{rootdir}/mp/guides/overview.adoc[MP Guides] -- -//Helidon SE +//Helidon Níma [CARD] -.Helidon SE +.Helidon Níma [icon=swap_horiz] -- diff --git a/docs/about/doc_sitemap.adoc b/docs/about/doc_sitemap.adoc new file mode 100644 index 00000000000..82d5c37265a --- /dev/null +++ b/docs/about/doc_sitemap.adoc @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Documentation Overview +:description: Helidon Documentation Sitemap +:keywords: helidon, java, microservices, microprofile, documentation, nima +:rootdir: {docdir}/.. + +== Using This Site + +Welcome to the Helidon documentation site. Here you will find general concepts, quickstart guides, tutorials and reference material. Existing Helidon users can go directly to their preferred programming flavor in the navigation panel to the left. + +=== New Users + +New to Helidon? Start with the xref:{rootdir}/about/introduction.adoc[Helidon Documentation] page. Here you will find some of the most common topics for getting started as well as easy first steps for building your first project or application. + +Once you've settled on your preferred programming framework, visit the Helidon flavor sections of this site for more information. + +* xref:{rootdir}/nima/introduction.adoc[Helidon Nima] +* xref:{rootdir}/se/introduction.adoc[Helidon SE] +* xref:{rootdir}/mp/introduction.adoc[Helidon MP] + + +=== Existing Helidon Users + +Welcome back! The documentation site has a couple of new features that we hope will make your experience here a bit easier. + +The new xref:{rootdir}/about/introduction.adoc[Helidon Documentation] page offers quick links to some of our most popular topics, including our new training and certification programs. + +Check out the xref:{rootdir}/about/intro.adoc[What's New in Helidon 4] for a list of new features and functionality. + + + diff --git a/docs/images/openapi-ui-screen-capture-greeting-nima-expanded.png b/docs/images/openapi-ui-screen-capture-greeting-nima-expanded.png new file mode 100644 index 00000000000..372ca09df30 Binary files /dev/null and b/docs/images/openapi-ui-screen-capture-greeting-nima-expanded.png differ diff --git a/docs/images/openapi-ui-screen-capture-greeting-nima-start.png b/docs/images/openapi-ui-screen-capture-greeting-nima-start.png new file mode 100644 index 00000000000..29f47a63b95 Binary files /dev/null and b/docs/images/openapi-ui-screen-capture-greeting-nima-start.png differ diff --git a/docs/includes/nima.adoc b/docs/includes/nima.adoc index 36441490d4b..3246fd92a4e 100644 --- a/docs/includes/nima.adoc +++ b/docs/includes/nima.adoc @@ -23,9 +23,11 @@ ifndef::flavor-included[] ifndef::rootdir[:rootdir: {docdir}/..] include::{rootdir}/includes/attributes.adoc[] -:h1-prefix: Níma -:flavor-uc: Níma +:h1-prefix: Nima +:flavor-uc: Nima :flavor-lc: nima +:nima-flavor: true +include::{rootdir}/includes/pages.adoc[] endif::flavor-included[] diff --git a/docs/includes/openapi/openapi.adoc b/docs/includes/openapi/openapi.adoc index 12a29d9161b..3d704d476bd 100644 --- a/docs/includes/openapi/openapi.adoc +++ b/docs/includes/openapi/openapi.adoc @@ -25,6 +25,7 @@ The link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] explains ho 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.] +ifdef::nima-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: diff --git a/docs/includes/pages.adoc b/docs/includes/pages.adoc index fc815ed783b..a28ae1be0a9 100644 --- a/docs/includes/pages.adoc +++ b/docs/includes/pages.adoc @@ -25,6 +25,12 @@ ifdef::mp-flavor[] :metrics-page: {rootdir}/mp/metrics/metrics.adoc :openapi-page: {rootdir}/mp/openapi/openapi.adoc endif::[] -:webclient-page: {rootdir}/se/webclient.adoc +ifdef::nima-flavor[] +:health-page: {rootdir}/nima/health.adoc +:metrics-page: {rootdir}/nima/metrics/metrics.adoc +:openapi-page: {rootdir}/nima/openapi/openapi.adoc +endif::[] + +:webclient-page: {rootdir}/nima/webclient.adoc :restclient-page: {rootdir}/mp/restclient.adoc :cli-page: {rootdir}/about/cli.adoc \ No newline at end of file diff --git a/docs/mp/cors/cors.adoc b/docs/mp/cors/cors.adoc index 6841726ea94..db908877921 100644 --- a/docs/mp/cors/cors.adoc +++ b/docs/mp/cors/cors.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2022 Oracle and/or its affiliates. + Copyright (c) 2022, 2023 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -72,7 +72,7 @@ You can do both. CORS configuration for a resource overrides any CORS settings d Adding CORS behavior to your Helidon MP application involves just a few simple steps. -For reach resource class in your application: +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: diff --git a/docs/nima/aot.adoc b/docs/nima/aot.adoc new file mode 100644 index 00000000000..9b9d3d2bb0f --- /dev/null +++ b/docs/nima/aot.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += GraalVM Native Image +:description: Helidon AOT using GraalVM native-image +:keywords: helidon, aot, native, native-image, image, executable, nima +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc + + +== TO DO FOR NIMA + diff --git a/docs/nima/config/advanced-configuration.adoc b/docs/nima/config/advanced-configuration.adoc new file mode 100644 index 00000000000..64fbc31a86c --- /dev/null +++ b/docs/nima/config/advanced-configuration.adoc @@ -0,0 +1,820 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Advanced Configuration Topics +:description: Helidon advanced configuration +:keywords: helidon, config, meta +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +This section discusses several advanced topics related to Helidon configuration. + +== Advanced Config Sources +=== Environment Variables Config Source + +The config system supports using environment variables as a config source, and is +enabled by default. Since environment variable names are normally restricted to +alphanumeric characters and underscore, this config source _adds_ aliases that +enable setting or overriding config entries with dotted and/or hyphenated keys. + +The mapping makes it possible to set or override a config entry with a key of +`"foo.bar"` using an environment variable named `"FOO_BAR"` and `"foo.bar-baz"` +using `"FOO_BAR_dash_BAZ"`. + + +One use case for this mapping is config overrides in containers, where passing +environment variables directly or via Kubernetes Secrets/ConfigMaps is common. +Scripts that solve the mapping problem by explicitly converting variables to +system properties can also be simplified. + +Aliases are produced for any environment variable name that matches _all_ of +the following: + +. does not begin or end with a `'_'` character +. does not contain `"__"` +. contains one or more `'_'` characters + +For each such name, two aliases are added with the names mapped as follows: + +. Replace any `"\_dash_"` or `"\_DASH_"` substrings with `"-"`, e.g. `"APP_PAGE_dash_SIZE"` +becomes `"APP_PAGE-SIZE"`. +. Replace `'_'` with `'.'` and add as an alias, e.g. `"APP_GREETING"` is +added as `"APP.GREETING"` and `"APP_PAGE-SIZE"` is added as `"APP.PAGE-SIZE"`. +This mapping is added primarily to support mixed case config keys such as +`"app.someCamelCaseKey"`. +. Convert the result of step 2 to lowercase and add as an alias, e.g. +`"APP.GREETING"` is added as `"app.greeting"` and `"APP.PAGE-SIZE"` is added +as `"app.page-size"`. + +=== Directory Config Source + +The config system supports using a file system directory as a config source. +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. + +The following example shows, for example, one way to load Kubernetes secrets +mounted on the pod's filesystem. + +If the directory `conf/secrets` contains these two files + +[source] +.File `secrets/username` +---- +jose +---- + +[source] +.File `secrets/password` +---- +^ery$ecretP&ssword +---- + +your application can load this as configuration as follows: + +[source,java] +.Using `directory` config source +---- +Config secrets = Config.builder( + ConfigSources.directory("conf/secrets")) // <1> + .disableEnvironmentVariablesSource() // <2> + .disableSystemPropertiesSource() // <2> + .build(); + +assert secrets.get("username") // <3> + .asString() + .get() + .equals("jose"); +assert secrets.get("password") // <4> + .asString() + .get() + .equals("^ery$ecretP&ssword"); +---- + +<1> Loads all files from the `conf/secrets` directory. +<2> No need to use environment variables or system properties as sources in building + the `Config`. +<3> The loaded config maps the key `username` to the value `jose`... +<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: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. + +=== In-memory Config Sources +The config system provides several ways to create a `Config` tree from data +already in memory. See the link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html[`ConfigSources` javadoc] +for further details. The numerous variants of the `from` method construct +`ConfigSource` or `Builder` instances. + +==== Subtree of Another `Config` +[source,java] +---- +Config anotherConfig = Config.create(classpath("application.conf")); + +Config config = Config.create( + ConfigSources.create(anotherConfig.get("data"))); +---- +==== `Properties` Object +[source,java] +---- +Config config = Config.create( + ConfigSources.create(System.getProperties()).build()); // <1> +---- + +==== `String` of a Given Media Type +[source,java] +---- +Config config = Config.create( + ConfigSources.create("app.greeting = Hi", "text/x-java-properties")); // <2> +---- +==== `Map` +[source,java] +---- +Config config = Config.crate( + ConfigSources.create(Map.of("app.page-size", "20")) + .lax() // <3> + .build()); // <1> +---- +==== _ad hoc_ Config Nodes +[source,java] +---- +Config config = Config.create( + ConfigSources.create(ObjectNode.builder() + .addList("app.basic-range", ListNode.builder() + .addValue("-20") + .addValue("20") + .build()) + .build())); +---- +<1> `ConfigSources.create` variants for `Properties` or `Map` arguments return a + `ConfigSources.MapBuilder` instance. +<2> A similar `create` variant accepts a `Readable` instead of a `String`. +<3> `MapBuilder` by default throws an exception if a key appears more than once +in the map. The `lax()` method relaxes this; the config system logs a warning instead. + +=== Multi-Source ``Config``s and Composite Config Sources +Although the examples above use a single source, you can build a single `Config` +from multiple sources. + +==== Handling Key Collisions +===== Prefixed Config Sources +Sometimes you might want to create a single config tree from +multiple sources but in a way that keeps the config from different sources +in different subtrees. + +The config system lets you assign a prefix to all keys +from a given source using the +link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html#prefixed-java.lang.String-java.util.function.Supplier-[`ConfigSources.prefixed`] method. +The following example shows two YAML files as config sources +and the code to load each with a different prefix into a single `Config` tree: + +[source,hocon] +.File `app.conf` +---- +greeting = "Hello" +page-size = 20 +basic-range = [ -20, 20 ] + +---- + +[source,hocon] +.File `data.conf` +---- +providers: [ + { + name = "Provider1" + class = "this.is.my.Provider1" + }, + { + name = "Provider2" + class = "this.is.my.Provider2" + } +] + +---- + +[source,java] +.Using `prefixed` config source +---- +Config config = Config.create( + ConfigSources.prefixed("app", // <1> + classpath("app.conf")), // <2> + ConfigSources.prefixed("data", // <3> + classpath("data.conf"))); // <4> + +assert config.get("app.greeting") // <5> + .asString() + .get() + .equals("Hello"); + +assert config.get("data.providers.0.name") // <6> + .asString() + .get() + .equals("Provider1"); +---- + +<1> Specifies the prefix `app` for the associated source. +<2> `Supplier` for the file + `app.conf` loaded from the current `classpath`. +<3> Specifies the prefix `data` for the associated source. +<4> Supplier for the file `app.conf` loaded from the current `classpath`. +<5> Key `app.greeting` combines the `app` prefix and the original key `greeting` +from the `app.conf` source. +<6> Key `data.providers.0.name` combines the `data` prefix and + the original key `providers.0.name` property from `data.conf` source. + +This technique can be useful, for example, if multiple sources contain +keys that might overlap; assigning different prefixes to the keys from different +sources gives your application a way to access all config elements distinctly even +if their keys would otherwise conflict. + +===== Merging Strategies +The `ConfigSources.create(Supplier...)` and `ConfigSources.create(List...)` +methods return a `CompositeBuilder`. +By default, earlier sources in the list have higher priority than later ones, meaning +that if the same key appears in two or more sources the source earlier in the +list prevails. + +Each ``CompositeConfigSource``'s _merging strategy_ actually controls this behavior. +The config system provides the +`FallbackMergingStrategy` +which implements the default, "first wins" algorithm. You can write your own +implementation of +link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.MergingStrategy.html[`ConfigSources.MergingStrategy`] +and use it instead to provide a different algorithm. + +[source,java] +.Composite config source example +---- +Config config = Config.create( // <1> + ConfigSources.create(file("conf/dev.properties").optional(), // <2> + file("conf/config.properties").optional()) // <2> + .add(classpath("application.properties")) // <3> + .mergingStrategy(ConfigSources.MergingStrategy.fallback())); // <4> +---- + +<1> Creates a new `Config` instance from a single composite config source. +<2> Method `ConfigSources.create(sources...)` returns `CompositeBuilder` instance + initialized with two sources (from `dev.properties` and `config.properties` + files). +<3> Adds third config source (`application.properties` on + classpath) to the same `CompositeBuilder`. +<4> Specifies the merging strategy. This example uses the default fallback + merging strategy. + +== 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. + +=== 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. + +==== Identifying the Media Type + +===== By Inference +Most applications let the config system try to infer the media type of the +config source. + +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.) + +===== By Application Directive +Your application can specify what media type to use in interpreting a config +source. Use this if your application knows the media type but the system might +not be able to infer it correctly, either because no type detector would recognize it or +because there might be more than one inferred media type. + +[source,java] +.Specify `mediaType` for config source +---- +Config config = Config.create(classpath("props") // <1> + .mediaType("text/x-java-properties")); // <2> +---- + +<1> The config system cannot infer the media type because there is no file +type in the path `props`. +<2> The developer knows the file is in Java Properties format so specifies the +media type explicitly. + +Note that a file type detector _could_ be written to +also inspect the contents of the file to infer the media type. The detectors +provided by Helidon only inspect the suffix in the name of the file. + +==== Locating a Parser [[locating-parser]] +===== By Inference from `media-type` +Each config parser reports which media types it handles. Once the config system +has determined a source's media type, it searches the config parsers associated +with the config builder for one that recognizes that media type. It then uses +that parser to translate the config in the source into the in-memory config tree. + +The application can add one or more parsers to a `Config.Builder` +using the `addParser` method. This makes the parser available for use by the +config sources associated with that builder, but does not directly tie a given +parser to a given source. The builder uses media-type matching to select one of +the parsers registered with the builder for each source. + +If the config system cannot locate a parser that matches the media type of a source, it throws +a `ConfigException` when trying to prepare the configuration. + +===== By Application Directive +Your application can specify which parser to use for a config source. The +`AbstractParsableConfigSource.Builder` class exposes the `parser` method, which +accepts the `ConfigParser` to be used for that source. Several methods +on `ConfigSources` such as `classpath`, `directory`, and `file` return this +builder class. + +Generally try to rely on media-type matching rather than specifying a given parser +for a given source in the application. This keeps your application more flexible, +both by insulating it from implementation classes and by letting it easily take +advantage of improvements in or alternatives to the parsers available for a given +media type. + +[source,java] +.Specify `parser` for config source +---- +Config config = Config.create(classpath("props") // <1> + .parser(ConfigParsers.properties())); // <2> +---- + +<1> The config system cannot infer the media type because there is no file +type in the path `props`. +<2> The developer knows the file is in Java Properties format so specifies the +properties parser explicitly. + +=== Parsing a Config Value as Config +A config value node might contain an entire config document in `String` form, but in +a format different from the containing document. Your application can tell the +config system to parse such a node as config in a different format and replace +the `String` value node in the original tree with the config tree that results +from parsing that `String`. + +In this example, a YAML document contains a JSON document as a leaf. + +[source,yaml] +.YAML file with included JSON formatted property +---- +secrets: + username: "jose" + password: "^ery$ecretP&ssword" + +app: > # <1> + { + "greeting": "Hello", + "page-size": 20, + "basic-range": [ -20, 20 ] + } + +---- + +<1> The property `app` is itself formatted as a JSON document. + +==== Specify Key-to-media-type Mapping +[source,java] +.Specify JSON as media type for node +---- +Config config = Config.create( + classpath("application.yaml") + .mediaTypeMapping( // <1> + key -> "app".equals(key.toString()) // <2> + ? "application/json" + : null)); + +assert config.get("secrets.username").asString() // <3> + .get().equals("jose"); +assert config.get("secrets.password").asString() // <3> + .get().equals("^ery$ecretP&ssword"); + +assert config.get("app").type() == Type.OBJECT; // <4> + +assert config.get("app.greeting") // <3> + .asString().get().equals("Hello"); +assert config.get("app.page-size") // <3> + .asInt().get() == 20; +assert config.get("app.basic-range.0") // <3> + .asInt().get() == -20; +assert config.get("app.basic-range.1") // <3> + .asInt().get() == 20; +---- + +<1> The source builder's `mediaTypeMapping` method accepts a function +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 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 +which also handles the JSON media type. + +Also, note that the config system replaces the original `String` value node with +an object node resulting from parsing that `String` value as JSON. + +==== Specify Key-to-parser Mapping +Alternatively, your application could map config keys to the specific parsers +you want to use for parsing those keys' values. + +[source,java] +.Specify JSON formatted property' parser instance +---- +Config config = Config.create( + ConfigSources.classpath("application.yaml") + .parserMapping( // <1> + key -> "app".equals(key.toString()) // <2> + ? Optional.of(HoconConfigParser.create()) + : Optional.empty())); +---- + +<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. (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. + +== Config Keys with . in name + +As described in the xref:hierarchical-features.adoc#accessByKey[hierarchical features +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`". +========= + +For example, the following configuration file contains two object nodes with +names `oracle` and `oracle.com`. + +[source,json] +.Example `application.json` with dot character in key +---- +{ + "oracle" : { + "com" : true, + "cz" : false + }, + "oracle.com" : { + "secured" : true + } +} + +---- + +[source,java] +.Working with configuration with dot character in node name +---- +Config config = Config.create(classpath("application.json")); + +// node `oracle` +assert config.get("oracle.com").asBoolean().get() == true; // <1> +assert config.get("oracle").get("com").asBoolean().get() == true; // <1> +assert config.get("oracle.com").type() == Type.VALUE; // <2> +assert config.get("oracle.com").name().equals("com"); // <3> +// node `oracle.com` +assert config.get("oracle~1com.secured").asBoolean().get() == true; // <4> +assert config.get(Key.escapeName("oracle.com")) // <5> + .get("secured").asBoolean().get() == true; +assert config.get(Key.escapeName("oracle.com")).type() == Type.OBJECT; // <6> +assert config.get(Key.escapeName("oracle.com")).name().equals("oracle.com"); // <7> +---- + +<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. +<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 +dot in the node's name using `oracle~1com`. +<5> Or, use the utility method `Config.Key.escapeName(name)` to escape dots or tildes +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"`. + +== Filters, 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 +_tokens_. The config system provides some built-in instances of these +you can use, and you can add your own as described in the +sections which describe +xref:extensions.adoc#Config-SPI-ConfigFilter[filters] and +xref:extensions.adoc#Config-SPI-OverrideSource[overrides]. + +Your application can add filters and overrides explicitly to a config builder +and the config system by default uses the Java service loader mechanism to +locate all available filters and overrides and add them automatically to all +config builders (unless your code disables that behavior for a given +builder). + +=== Filters +Each filter accepts a key and the value as defined in the source, and returns +the value to be used. The filter can leave the value unchanged or +alter it, as it sees fit. + +The built-in link:{config-javadoc-base-url}/io/helidon/config/ConfigFilters.html#valueResolving--[value-resolving] +filter enables the token substitution described below. + +See the link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigFilter.html[`ConfigFilter`] JavaDoc +for more information. + +=== Overrides +The overrides feature allows you to create an external document containing key/value +pairs which replace the value otherwise returned for the name, and then add that +document as an override source to a config builder. + +There are some key differences between overrides and filters. + +* Because overrides are loaded +from sources those sources can change while your application runs and so the +overrides they that prescribe can change. +* The override document can use wildcards in key expressions. +* Overrides can affect only keys that already exist in the original source; filters +can supply values even if the key is absent from the config source. + +Each override entry consists of a Java properties-format definition. The key is an +expression (which can use wildcards) to match config keys read from the current +config sources, and the override value is the new value for any key matching the +key expression from that entry. Order is important. The +config system tests every key expression/value pair one by one in the order they appear +in the overrides sources. Once the config system finds an override entry in which +the key expression matches the configuration key, the system returns that entry's +value for the key being processed. + +See the link:{config-javadoc-base-url}/io/helidon/config/spi/OverrideSource.html[`OverrideSource]` JavaDoc +for more detail. + +=== Tokens +A token reference is a key token starting with `$`, optionally enclosed between + `{` and `}`, i.e. `$ref`, `${ref}`. Even a key composed of more than one token + can be referenced in another key, i.e. `${env.ref}`. + +As an example use case, you can use token references to declare the default values (see +`resolving-tokens.yaml` below), while the references may be resolved in another +config source, which identifies a current environment (see `env.yaml` examples +below). You can then use the same overrides for different environments, say `test` and `prod`. +The configuration in each environment is then overridden with a different values +using wildcards (see `overrides.properties` below). + +[source,java] +.Initialize `Config` with Override Definition from `overrides.properties` file +---- +Config config = Config.builder() + .overrides(OverrideSources.file("conf/overrides.properties")) // <1> + .sources(file("conf/env.yaml"), // <2> + classpath("resolving-tokens.yaml")) // <3> + .build(); +---- + +<1> Loads _overrides_ from the specified file. +<2> A deployment-specific environment configuration file. +<3> A default configuration containing token references that are resolved +using the environment-specific override. + +You can disable key and value token replacement separately as the following example shows. + +[source,java] +.Disabling Key and Value Token Replacement +---- +Config config = Config.builder() + .disableKeyResolving() + .disableValueResolving() + // other Config builder settings + .build(); +---- + +== Executors for Asynchronous Config Activity +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, +* `Config` instances which subscribe to and respond to change notifications for +their underlying sources, and +* retry policies (which might wait between retries). + +Each of these uses an executor to perform its work. The config system provides default +executors, but your application can specify different ones if necessary. + +=== Executors for Polling Strategy +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 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 +strategy instances. +[source,java] +.Customize polling strategy executors +---- +ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); // <1> + +Config config = Config.create( + ConfigSources.file("conf/dev.properties") + .pollingStrategy( + PollingStrategies.regular(Duration.ofSeconds(2)) // <2> + .executor(executor)), // <3> + ConfigSources.create("conf/config.properties") + .pollingStrategy( + path -> PollingStrategies.watch(path) // <4> + .executor(executor))); // <5> +---- + +<1> Prepares a thread pool executor with core pool size set `2` to be shared by + all polling strategies. +<2> Selects the built-in periodic polling strategy. +<3> Tells the config system to use the specific executor to poll the +`dev.properties` config source. +<4> Uses the Java filesystem `WatchService` to monitor the specified path. +<5> Tells the config system to use the same executor to monitor the path. + +=== 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 +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. + +Your application can invoke the polling strategy builder's `changesExecutor` method to +tell the builder +to use a different `Executor`. (As an aside, your application can also control +the size of the buffer used for holding source change events by invoking the +builder's `changesMaxBuffer` method. The default is 256.) + +[source,java] +.Customize config and override sources' executors +---- +Executor executor = Executors.newCachedThreadPool(); // <1> + +Config config = Config.builder() + .overrides( + OverrideSources.file("conf/overrides.properties") + .pollingStrategy(PollingStrategies::watch) + .changesExecutor(executor) // <2> + .changesMaxBuffer(4)) // <3> + .sources( + ConfigSources.file("conf/env.yaml") + .pollingStrategy(PollingStrategies::watch) + .changesExecutor(executor) // <4> + .changesMaxBuffer(4)) // <4> + .build(); +---- + +<1> Prepares a thread pool executor to be shared by selected sources. +<2> Tells the builder that the resulting overrides source should use the specified +`Executor` for notifying interested parties of changes and for reloading the +override source. +<3> Specifies an event buffer size of 4. +<4> Uses the same `Executor` and event buffer size for the config source as for +the override source above. + +=== Composite Config Source Executor +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 +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. + +Your application can invoke the builder's `changesExecutor` method to use a +different `ScheduledExecutorService` instance. +The builder returned by the `from` methods mentioned above is a +link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.CompositeBuilder.html[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 _debounce timeout_ +for the composite source by invoking the `CompositeBuilder.changesDebounce(Duration)` +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. + +[source,java] +.Customize composite source executors +---- +ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); // <1> + +Config config = Config.create( + ConfigSources.create(file("conf/dev.properties") // <2> + .pollingStrategy(PollingStrategies::watch), + file("conf/config.properties") // <2> + .pollingStrategy(PollingStrategies::watch)) + .changesExecutor(executor) // <3> + .changesMaxBuffer(4) // <4> + .changesDebounce(Duration.ofSeconds(1))); // <5> +---- + +<1> Prepares a thread pool executor. +<2> `ConfigSources.create(Supplier...)` creates and returns a +`CompositeBuilder` based on the two sources. +<3> Specifies a particular executor for monitoring and change event notification. +<4> Sets the subscriber's buffer size to 4 events. The composite source discards +any events not consumed by a subscriber if it needs to create room for more +recent events. +<5> Change events will not fire more frequently than once per a second. + +=== Config Custom Executor +A loaded config tree subscribes to change events publishes by its source(s). +By default, each `Config` uses an executor which manages a dedicated thread pool +reusing previously-created threads when they are available and creating new threads +as needed. +All `Config` instances share the dedicated thread pool. + +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. + +[source,java] +.Customize config executor +---- +Executor executor = Executors.newCachedThreadPool(); // <1> + +Config config = Config.create( + file("conf/config.properties") + .pollingStrategy(PollingStrategies::watch)) + .changesExecutor(executor) // <2> + .changesMaxBuffer(16) // <3> + .build(); +---- + +<1> Prepares a specific thread pool executor. +<2> Specifies the executor the `Config` tree will use to listen for and propagate +change events. +<3> Sets the event subscriber buffer to `16` events. + +=== Retry Policy Custom Executor +You can control which executor a retry policy should use for its work. +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 +pool executor for each retry policy instance. + +[source,java] +.Customize retry policy executors +---- +ScheduledExecutorService executor = Executors.newScheduledThreadPool(2, myThreadFactory); // <1> + +Config config = Config.create( + ConfigSources.file("conf/dev.properties") + .optional() // <2> + .retryPolicy(RetryPolicies.repeat(2) // <3> + .executor(executor))); // <4> +---- + +<1> Prepares a thread pool executor with core pool size set to `2` and a custom + `java.util.concurrent.ThreadFactory`. +<2> When the source is flagged as `optional()`, the loading attempt will be + repeated as the retry policy defines, but an overall failure will _not_ lead to +failing the initial load or preventing the source from being polled if so configured. +<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/nima/config/config-profiles.adoc b/docs/nima/config/config-profiles.adoc new file mode 100644 index 00000000000..5b5547774d2 --- /dev/null +++ b/docs/nima/config/config-profiles.adoc @@ -0,0 +1,316 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Configuration Profiles +:description: Helidon config profiles +:keywords: helidon, config, profile +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> + +== 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. + +== Profile options +To choose a configuration profile to use at runtime, you can use: + +1. A system property `config.profile` +2. An environment variable `HELIDON_CONFIG_PROFILE` + +There are two ways to define a profile configuration: + +1. Use a config source with a <> +2. Use a <> defining all configuration sources + +Configuration profiles can only be used when config is created using the `Config.create()` method without parameters. If you explicitly configure sources, profiles are ignored. + +== Profile Config Sources [[Profile-Config-Source]] + +If a profile is specified, config will load the profile-specific default configuration source +before the "main" source. + +Let's consider the selected profile is `dev`, and we have `yaml` configuration support on classpath; +config will look for the following sources (in this order): + +1. `application-dev.yaml` on file system +2. `application-dev.properties` on file system +3. `application-dev.yaml` on classpath +4. `application-dev.properties` on classpath +5. `application.yaml` on file system +6. `application.properties` on file system +7. `application.yaml` on classpath +8. `application.properties` on classpath + +== Profile Files [[Profile-File]] + +If a profile is specified, config will look for a profile-specific "meta configuration". + +Let's consider the selected profile is `dev`, and we have `yaml` configuration support on classpath; +config will look for the following profiles (in this order): + +1. `config-profile-dev.yaml` on file system +2. `config-profile-dev.properties` on file system +3. `config-profile-dev.yaml` on classpath +4. `config-profile-dev.properties` on classpath + +If any of these files is discovered, it would be used to set up the configuration. In case none is found, + the config falls back to <>. + +The structure of the file is described below in <>. + +In case you need to customize the location of the profile file, you can use the system property +`io.helidon.config.meta-config`. For example if it is configured to `config/profile.yaml`, +config looks for file `config/profile-dev.yaml` when `dev` profile is configured. + +=== Profile File Format [[Config-Profile-Format]] + +Configuration profile provides similar options to the configuration builder. +The profile file must contain at least the list of sources from which configuration can be loaded. + +The root `sources` property contains an array (ordered) of objects defining each config source to +be used. +Each element of the array must contain at least the `type` property, determining the +config source type (such as `system-properties`, `file`). It may also contain a `properties` +property with additional configuration of the config source. + +An example development profile using "inlined" configuration: +[source,yaml] +.Config profile `config-profile-dev.yaml` +---- +sources: + - type: "inlined" + properties: + app.greeting: "Hello World" +---- + +An example of a profile using environment variables, system properties, classpath, and file configuration: +[source,yaml] +.Config profile `config-profile-prod.yaml` +---- +sources: + - type: "environment-variables" + - type: "system-properties" + - type: "file" + properties: + path: "config/config-prod.yaml" + - type: "classpath" + properties: + resource: "application.yaml" +---- + +==== Built-in Types +The config system supports these built-in types: + +.Built-in Types +|=== +|Type |Use |Related `ConfigSources` Method |Required Properties + +|`system-properties` |System properties are a config source |`ConfigSources.systemProperties()` | n/a +|`environment-variables` |Environment variables are a config source |`ConfigSources.environmentVariables()` | n/a +|`classpath` |Specified resource is used as a config source |`ConfigSources.classpath(String)` | `resource` - path to the resource to load +|`file` |Specified file is used as a config source |`ConfigSources.file(Path)` |`path` - path to the file to load +|`directory` |Each file in directory used as config entry, with key = file name and value = file contents |`ConfigSources.directory(String)` |`path` - path to the directory to use +|`url` |Specified URL is read as a config source |`ConfigSources.url(URL)` | `url` - URL from which to load the config +|`inlined` |The whole configuration tree under `properties` is added as a configuration source (excluding the `properties` node) |n/a |n/a +|`prefixed` |Associated config source is loaded with the specified prefix |`ConfigSources.prefixed(String,Supplier)` a|* `key` - key of config element in associated source to load +* `type` - associated config source specification +* `properties` - as needed to further qualify the associated config source +|=== + +Except for the `system-properties` and `environment-variables` types, the profile +`properties` section for a source can also specify any optional settings for the +corresponding config source type. The JavaDoc for the related config source +type builders lists the supported properties for each type. (For example, +link:{config-javadoc-base-url}/io/helidon/config/internal/FileConfigSource.FileBuilder.html[`FileConfigSource.FileBuilder`].) + +Here is an example profile in YAML format. Note how the `properties` sections +are at the same level as the `type` or `class` within a `sources` array entry. + +[source,yaml] +.Profile `config-profile.yaml` illustrating all built-in sources available on the classpath +---- +caching.enabled: false +sources: + - type: "system-properties" + - type: "environment-variables" + - type: "directory" + properties: + path: "conf/secrets" + media-type-mapping: + yaml: "application/x-yaml" + password: "application/base64" + polling-strategy: + type: "regular" + properties: + interval: "PT15S" + - type: "url" + properties: + url: "http://config-service/my-config" + media-type: "application/hocon" + optional: true + retry-policy: + type: "repeat" + properties: + retries: 3 + - type: "file" + properties: + optional: true + path: "conf/env.yaml" + change-watcher: + type: "file" + properties: + delay-millis: 5000 + - type: "prefixed" + properties: + key: "app" + type: "classpath" + properties: + resource: "app.conf" + - type: "classpath" + properties: + resource: "application.conf" +---- + +Note that the example shows how your profile can configure optional features such as polling +strategies and retry policies for config sources. + +==== Support for Custom Sources +Profiles can be used to set up custom config sources as well as the built-in ones described above. + +Implement the `ConfigSourceProvider` +[source,java] +---- +public class MyConfigSourceProvider implements ConfigSourceProvider { + private static final String TYPE = "my-type"; + + @Override + public boolean supports(String type) { + return TYPE.equals(type); + } + + @Override + public ConfigSource create(String type, Config metaConfig) { + // as we only support one in this implementation, we can just return it + return MyConfigSource.create(metaConfig); + } + + @Override + public Set supported() { + return Collections.singleton(TYPE); + } +} +---- + +Register it as a java service loader service +[source] +.File `META-INF/services/io.helidon.config.spi.ConfigSourceProvider` +---- +io.helidon.examples.MyConfigSourceProvider +---- + +And in `module-info.java` if using JPMS: +[source,java] +.File `module-info.java` +---- +provides io.helidon.config.spi.ConfigSourceProvider with io.helidon.examples.MyConfigSourceProvider +---- + +Now you can use the following profile: +[source,yaml] +---- +sources: + - type: "system-properties" + - type: "environment-variables" + - type: "my-type" + properties: + my-property: "some-value" +---- + +Note that it is the `io.helidon.config.AbstractConfigSource` class that provides support for +polling strategies, change watchers, and retry policies. If you create custom config sources that +should also offer this support be sure they extend `AbstractConfigSource` and implement appropriate +SPI interfaces (such as `io.helidon.config.spi.WatchableSource`) to support such features. + +==== Support for Custom Polling Strategies, Change Watchers, and Retry Policies + +Your config profile can include the set-up for polling strategies, change watchers, and retry +policies if the config source supports them. Declare them in a way similar to +how you declare the config sources themselves: by `type` and with +accompanying `properties`. + +.Config Profile Support for Built-in Polling Strategies +|=== +|Strategy Type |Usage |Properties + +|`regular` +| Periodic polling - See link:{config-javadoc-base-url}/io/helidon/config/PollingStrategies.html#regular-java.time.Duration-[`PollingStrategies.regular`] method +|`interval` (`Duration`) - indicating how often to poll; e.g., `PT15S` represents 15 seconds + +|=== + +.Config Profile Support for Built-in Change Watchers +|=== +|Type |Usage |Properties + +|`file` +| Filesystem monitoring - See link:{config-javadoc-base-url}/io/helidon/config/PollingStrategies.html#watch-java.nio.file.Path-[`PollingStrategies.watch`] method +| `initial-delay-millis` - delay between the start of the watcher and first check for changes + +|=== + +.Config Profile Support for Built-in Retry Policies +|=== +|Policy Type |Usage |Properties + +|`repeat` +|Regularly-scheduled - see link:{config-javadoc-base-url}/io/helidon/configRetryPolicies.html#repeat-int-[`RetryPolicies.repeat`]. +a|`retries` (`int`) - number of retries to perform + + +Optional: + +* `delay` (`Duration`) - initial delay between retries +* `delay-factor` (`double`) - `delay` is repeatedly multiplied by this each retry to compute +the delay for each successive retry +* `call-timeout` (`Duration`) - timeout for a single invocation to load the source +* `overall-timeout` (`Duration`) - total timeout for all retry calls and delays +|=== + +To specify a custom polling strategy or custom retry policy, implement the interface +(`io.helidon.config.spi.PollingStrategy`, `io.helidon.config.spi.ChangeWatcher`, + or `io.helidon.config.spi.RetryPolicy`), and then implement the provider interface +(`io.helidon.config.spi.PollingStrategyProvider`, `io.helidon.config.spi.ChangeWatcherProvider`, or +`io.helidon.config.spi.RetryPolicyProvider`) to enable your custom implementations for +profiles. +You can then use any custom properties - these are provided as a `Config` instance to +the `create` method of the Provider implementation. + +See link:{config-javadoc-base-url}/io/helidon/config/spi/RetryPolicy.html[`RetryPolicy`], +link:{config-javadoc-base-url}/io/helidon/config/spi/RetryPolicy.html[`ChangeWatcher`], and +link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`] JavaDoc +sections. \ No newline at end of file diff --git a/docs/nima/config/extensions.adoc b/docs/nima/config/extensions.adoc new file mode 100644 index 00000000000..5efe21096cb --- /dev/null +++ b/docs/nima/config/extensions.adoc @@ -0,0 +1,467 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Extensions +:description: Helidon config extensions +:keywords: helidon, config +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Developer-provided extensions influence how the config system behaves. + +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 +modify and expand the way the config system performs these steps. + +== Introduction +Each config extension implements one of the interfaces defined in the Configuration SPI: + +//Once our asciidoc processing handles labeled lists, uncomment the following +//and use it instead of the bulleted list which follows: +//`ConfigSource`:: Loads raw configuration data from a given type of source and +//delegates to a `ConfigParser`, producing the in-memory data structure which +//represents the loaded and parsed configuration. +//`ConfigParser`:: Translates configuration content in a given format into the +//corresponding internal config data structures. +//`OverrideSource`:: Provides key/value pairs which override config values loaded +//from any `ConfigSource`, given the key but _ignoring_ the original value. +//`ConfigFilter`:: Transforms config `String` values returned from any value-type +//`Config` node, given the key _and_ the original value. +//`ConfigMapperProvider`:: Provides one or more ``ConfigMapper``s each of which +//converts a `Config` object tree to a Java type specific to the application. +//`PollingStrategy`:: Implements a custom technique for notifying the Config system +//when the data underlying a `ConfigSource` or `OverrideSource` has changed. +* `ConfigSource` - Loads raw configuration data from a given type of source and delegates to a `ConfigParser`, producing the in-memory data structure which represents the loaded and parsed configuration. +* `ConfigParser` - Translates configuration content in a given format into the corresponding internal config data structures. +* `OverrideSource` - Provides key/value pairs which override config values loaded from any `ConfigSource`, given the key and _ignoring_ the original value. +* `ConfigFilter` - Transforms config `String` values returned from any value-type +`Config` node, given the key _and_ the original value. +* `ConfigMapperProvider` - Provides one or more ``ConfigMapper``s each of which converts a `Config` object tree to a Java type specific to the application. +* `PollingStrategy` - Implements a custom technique to trigger polling of underlying sources for changes +* `ChangeWatcher` - Implements a custom technique to watch underlying sources for changes and notifying the config system of such a change + +The extension mechanism of Config can also use Java `ServiceLoader`. +For this purpose, you implement providers that serve as factories for your implementation of an extension. +This is to support config profiles even for custom extensions. +Service providers: + +* `ConfigMapperProvider` - support for config mappers, automatically discovered by the config system +* `ConfigFilter` - support for config filters, automatically discovered by the config system +* `ConfigParser` - support for config parsers, automatically discovered by the config system +* `ConfigSourceProvider` - support for named config sources, configurable through profiles +* `ChangeWatcherProvider` - support for named change watchers, configurable through profiles +* `OverrideSourceProvider` - support for named override sources, configurable through profiles +* `PollingStrategyProvider` - support for named polling strategies, configurable through profiles +* `RetryPolicyProvider` - support for retry policies, configurable through profiles + +The config system itself implements several of these SPIs, as noted in the sections below. + +== Setting up an extension + +You can configure a custom extension in two ways: + +1. Manual configuration with builder +2. Automatic configuration using a Java service loader + +=== Manual configuration with builder + +The following example shows configuration of all possible extensions with `Config` (all custom extension have a name prefix `My`): + +[source,java] +---- +Config config = Config.builder() + .addSource(FileConfigSource.builder() + .changeWatcher(MyChangeWatcher.create()) + .pollingStrategy(MyPollingStrategy.create()) + .parser(MyConfigParser.create()) + .retryPolicy(MyRetryPolicy.create())) + .addSource(MySource.create()) + .addFilter(MyFilter.create()) + .overrides(MyOverrides.create()) + .build() +---- + +=== Automatic configuration using a service loader + +The following extensions are loaded using a service loader for any configuration instance, and do not require an explicit setup: + +* `ConfigParser` - each config parser on the classpath that implements `ConfigParserProvider` as a Java service loader service +* `ConfigFilter` - each filter on the classpath that implements `ConfigFilter` as a Java service loader service + +Other extensions are only used from Java service loader when you use config profiles. +Mapping is done through the type configured in config profile, and the type defined by the extension provider interface. +For example for config sources, the interface defines the following methods (only subset shown): + +[source,java] +---- +boolean supports(String type); +ConfigSource create(String type, Config metaConfig); +---- + +Considering the following meta configuration (or config profile): + +[source,yaml] +---- +sources: + - type: "my-type" + properties: + my-config: "configuration" +---- + +The config system would iterate through all `ConfigSourceProvider` implementations found through Java `ServiceLoader` based on their priority. +First provider that returns `true` when `supports("my-type")` is called would be used, and an instance of a `ConfigSource` created using `create("my-type", config)`, where `config` is located on the node of `properties` from config profile. + +=== About Priority [[priority-info]] + +The config system invokes extensions of a given type in priority order. +Developers can express the relative importance of an extension by annotating the service implementation class with +`@jakarta.annotation.Priority`. +The default value is 100. A _lower_ priority value represents _greater_ importance. + +== ConfigSource SPI [[Config-SPI-ConfigSource]] + +The config system includes built-in support for several types of sources +(for example, Java `String`, `Readable`, `Properties`, and `Map` +objects - see link:{config-javadoc-base-url}/io/helidon/config/ConfigSources.html[`ConfigSources`]). +Implement a link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigSource.html[`ConfigSource`] to +load raw configuration data from a type of source that the config system does +not already support. + +image::config/spi-ConfigSource.png[title="ConfigSource SPI",align="center"] + +For config sources that work directly with config nodes, the following API is available. +These interfaces have an implementation provided by Helidon. +The interfaces `ConfigNode`, `ObjectNode`, `ValueNode` and +`ListNode` represent the in-memory data structure for loaded and parsed configuration data. + +image::config/spi-node.png[title="ConfigNode SPI",align="center"] + +For config sources that work return data (`NodeConfigSource` and `ParsableConfigSource`) a +`Content` must be returned that describes the loaded data. +The following diagram depicts the `Content` API. + +image::config/spi-content.png[title="Content SPI",align="center"] + +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 +`empty` for sources, that do not implement `PollableSource` +* ConfigParser.Content.charset() - this can return any `Charset` for media types that are binary +* ConfigParser.Content.mediaType() - this can be used to override media type (that would otherwise be "guessed" from the underlying source) +* ParsableSource.parser() - this can be used to override parser (that would otherwise be based on `mediaType`) +* ParsableSource.mediaType() - return the configured or "guessed" media type of this source, see +`io.helidon.common.media.type.MediaTypes`, if not returned, media type must be present on `Content`, or provided through media type mapping + +== ConfigParser SPI [[Config-SPI-ConfigParser]] + +The parsing step converts config data in some format into the corresponding in-memory representation of config ``ObjectNode``s. +The config system can already parse several data formats (for example Java `Properties`, YAML, and HOCON). +Implement the +link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigParser.html[`ConfigParser`] SPI to allow the config system to handle additional formats. + +image::config/spi-ConfigParser.png[title="ConfigParser SPI",align="center"] + +The `ConfigParser.Content` interface defines operations on the content that is to be parsed by a `ConfigParser` implementation: + +* `mediaType()` - Reports the media type of the content (if it is to override media type defined on the config source) +* `data()` - Provides the `InputStream` with config source data +* `charset()` - Defines the charset to use to parse the stream in case this is a text based media type, ignored by parsers of binary content + + +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 autoloading of parsers for a given builder by invoking `Config.Builder#disableParserServices()`. + +`ConfigParser` accepts `@Priority`. +See <>. + +[source,listing] +.Example custom parser implementation listed in `META-INF/services/io.helidon.config.spi.ConfigParser` +---- +my.module.MyConfigParser +---- + +[source,java] +.Example custom parser definition in `module-info.java` +---- +module my.module { + requires transitive io.helidon.config; + provides io.helidon.config.spi.ConfigParser with myModule.MyConfigParser; +} +---- + +== OverrideSource SPI [[Config-SPI-OverrideSource]] + +When the application retrieves a configuration value the config system first uses +the relevant config sources and filters. It then applies any _overrides_ the +application has provided. Each override has: + +* a `Predicate` (a boolean-valued function that operates on +the config key), and +* a replacement, _overriding_, `String` value the config system should use if the predicate evaluates to true. + +To furnish overrides to the config system, implement the +link:{config-javadoc-base-url}/io/helidon/config/spi/OverrideSource.html[`OverrideSource`] SPI one or more times and pass instances of + those implementations to the config builder's +link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#overrides-java.util.function.Supplier-[`overrides`] method. +The config system will apply the overrides returned from each +`OverrideSource` to each config key requested from a `Config` that is based on that `Config.Builder`. + +To support custom override sources in config profiles, also implement the +link:{config-javadoc-base-url}/io/helidon/config/spi/OverrideSourceProvider.html[`OverrideSourceProvider`] service loader SPI + +image::config/spi-OverrideSource.png[title="OverrideSource SPI",align="center"] + +Note that override sources can also implement `PollableSource`, and `WatchableSource` to add change support. + +== ConfigFilter SPI [[Config-SPI-ConfigFilter]] + +Before returning a `String` from `Config.value()` the config system applies any _filters_ set up on the + `Config.Builder` used to create the config tree that contains the config node of interest. +The application provides filters as implementations of the +link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigFilter.html[`ConfigFilter`] interface. +Each filter is a function which accepts a `Config.Key` and an input `String` value and returns a `String` value + the config system should use for that key going forward. +The filter can return the original value or return some other value. + +The application registers filters and filter providers by passing `ConfigFilter` +implementations to one of the config builder +link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html[`addFilter` methods]. The config +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 autoloaded filters by invoking the +link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#disableFilterServices--[`disableFilterServices`] +method. + +.Config SPI Interfaces for Filtering +|=== +|Interface |Method |Usage + +|link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigFilter.html[`ConfigFilter`] + +Accepts `@Priority`. See <>. +|`String apply(Config.Key key, String stringValue);` +|Accepts a key and the corresponding `String` value and +returns the `String` which the config system should use for that key. +|=== + +=== Initializing Filters +The `ConfigFilter` JavaDoc describes multiple methods for adding filters to a +`Config.Builder`. Some accept a `ConfigFilter` directly and some accept a provider +function which, when passed a `Config` instance, returns a `ConfigFilter`. + +*_Neither a `ConfigFilter` nor a provider function which furnishes one should +access the `Config` instance passed to the provider function._* + +Instead, implement the `ConfigFilter.init(Config)` method on the filter. The config +system invokes the filters' `init` methods according to the filters' `@Priority` +order. + +Recall that whenever any code invokes `Config.get`, the `Config` instance +invokes the `apply` method of _all_ registered filters. By the time the application +retrieves config this way the config system will have run the `init` method on all +the filters. _But note that when a filter's `init` method invokes `Config.get`, the +`init` methods of lower-priority filters will not yet have run._ + +image::config/spi-ConfigFilter.png[title="ConfigFilter SPI",align="center"] + +== ConfigMapperProvider SPI [[Config-SPI-ConfigMapperProvider]] + +The config system provides built-in mappings from `String` values to various Java +types. (See link:{config-javadoc-base-url}/io/helidon/config/ConfigMappers.html[`ConfigMappers`].) + +To handle mappings to other types the application can register +custom mappers with the config system by implementing the +link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigMapperProvider.html[`ConfigMapperProvider`] + SPI. + +Such providers return a map, with entries in which: + +* the key is the Java type (a `Class` object) the mapper produces, and +* the value is a `ConfigMapper` that converts the config in-memory +data structure into the type in the key. + +The provider may also implement other methods for finer tuned conversion mechanisms: + +* `genericTypeMappers()` returns a map with entries for specific `GenericType` conversions, + for example when the provider supports only mapping for `GenericType>` +* `mapper(Class)` returns a conversion function (optional) that converts a config node + to the typed instance (if supported by this provider) +* `mapper(GenericType)` returns a conversion function (optional) that coverts a config node + to the GenericType (if supported by this provider) - for example in case this provider supports + any Map type, such as `Map` and `Map` + +The config conversion system works as follows: + +For `Config.as(Class)`: + +1. Check whether a conversion function exists for the class requested (from method `mappers()`). +2. Check whether a conversion function is provided by any `ConfigMapperProvider` with method `mapper(Class)`. +3. Check whether a conversion function exists for a generic type for the class requested (from method `genericTypeMappers`). +4. Check whether a conversion function is provided by any `ConfigMapperProvider` with method `mapper(GenericType)` for + a generic type for the class requested. + +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 autoloading of mappers for a +given builder by invoking `Config.Builder#disableMapperServices()`. Note +that the built-in mappers described in `ConfigMappers` still operate. + +Mapper providers accept `@Priority`. See <>. + +image::config/spi-ConfigMapperProvider.png[title="ConfigMapperProvider SPI",align="center"] + +A mapper provider can specify a `@jakarta.annotation.Priority`. +If no priority is explicitly assigned, the value of `100` is assumed. + +[source,java] +.Reference custom mapper provider implementation in `META-INF/services/io.helidon.config.spi.ConfigMapperProvider` +---- +my.module.MyConfigMapperProvider +---- + +[source,java] +.Reference custom mapper provider implementation in `module-info.java` +---- +module my.module { + requires transitive io.helidon.config; + provides io.helidon.config.spi.ConfigMapperProvider with my.module.MyConfigMapperProvider; +} +---- + +== Change support SPI [[Config-SPI-PollingStrategy]] + +Once it loads a `Config` tree from ``ConfigSource``s the config system does not itself change the in-memory `Config` + tree. Even so, the underlying data available via the tree's ``ConfigSource``s can change. +Implementations of link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`] +may trigger regular check whether a source has new data. +Implementation of link:{config-javadoc-base-url}/io/helidon/config/spi/ChangeWatcher.html[`ChangeWatcher`] +may watch the underlying source for changes and trigger an update. + +=== PollingStrategy SPI + +An implementation of `PollingStrategy` gets an instance to poll, and triggers its `poll` method. +The result of `poll` method may be used to update the polling strategy schedule. + +The approach of checking for changes is part of the config system, and the `PollingStrategy` does not need to be + concerned with it. This is based on the source `stamp` as defined in `ConfigContent` and used in +`PollableSource.isModified(Object)` methods. + +If a more sophisticated solution is needed, you may need to implement a `ChangeWatcher` instead. + +The config system offers polling strategy for periodic time-based checks. +Often an application can create a config source simply by using one of the methods on `ConfigSources` (for example, +`ConfigSources#file(path)` to get a builder and then invoke `pollingStrategy` +passing a polling strategy. +But the application can implement its own `PollingStrategy` and set it on the config source builder instead. + +image::config/spi-PollingStrategy.png[title="PollingStrategy SPI",align="center"] + +To support polling strategies that can be configured in config profile, also implement the `PollingStrategyProvider` + Java service loader SPI. + +=== ChangeWatcher SPI + +An implementation of `ChangeWatcher` gets the underlying source information and a change listener. +The "watcher" then watches for changes of the source and notifies the listener when a change occurs. + +This is designed to support sources that can react on changes (such as file system). +When a polling mechanism is needed, please check `PollingStrategy` above. + +The config system offers a change watcher for any `Path` based config source (such as `FileConfigSource`) and for the + `etcd` config source. + +To use a change watcher, simply create a config source using its builder and register the change watcher on the builder + (the config source must support appropriate type of change watchers). + +image::config/spi-ChangeWatcher.png[title="ChangeWatcher SPI",align="center"] + +To support change watchers that can be configured in config profile, also implement the `ChangeWatcherProvider` Java + service loader SPI. + +== RetryPolicy SPI [[Config-SPI-RetryPolicy]] + +The builder for each `ConfigSource` and `OverrideSource` accepts a +link:{config-javadoc-base-url}/io/helidon/config/spi/RetryPolicy.html[`RetryPolicy`] +governing if and how the source should deal with failures loading the underlying +data. + +A retry policy accepts a function, the invocation of which the policy will +govern according to its own implementation. +Applications can use the predefined policies in +link:{config-javadoc-base-url}/io/helidon/config/RetryPolicies.html[`RetryPolicies`], such as +`RetryPolicies.justCall` which simply invokes the function without any retry. +That class also exposes a builder for constructing a time-based retry policy, +with several parameters: + +.Parameters Controlling Built-in `RetryPolicy` +|=== +|Parameter |Usage |Default + +|`delay` |Initial delay between calls to the function | 200 ms +|`delayFactor` |Multiplier applied to `delay` on each successive call | 2 +|`callTimeout` |Time limit for each individual call of the function | 500 ms +|`overallTimeout` |Limit for the total elapsed time attempting to +call the function successfully, including delays between calls | 2 s +|=== + +The actual delay between function call starts as `delay` and changes by the factor +`delayFactor` on each successive attempt. + +Note that the job of each retry policy is to call the provided function +successfully. As such, the policy must perform the first attempt as well +as any retries. + +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 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. + +To support retry policies in config profiles, also implement the Java service loader SPI +`RetryPolicyProvider`. + diff --git a/docs/nima/config/hierarchical-features.adoc b/docs/nima/config/hierarchical-features.adoc new file mode 100644 index 00000000000..f4f7f944e77 --- /dev/null +++ b/docs/nima/config/hierarchical-features.adoc @@ -0,0 +1,364 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Hierarchical Features +:description: Helidon hierarchical features +:keywords: helidon, config +:feature-name: Config +:rootdir: {docdir}/../.. +:imagesdir: {rootdir}/images + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== 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 +explicitly among the nodes of the tree without using keys. +This section describes what the tree looks like and how you can traverse +it. + +== Configuration Node Types +The config system represents configuration in memory using three types of nodes, +each a different interface +defined within the link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigNode.html[`ConfigNode`] interface. + +.`ConfigNode` Types +|=== +|Type | Java Interface | Usage + +|object |`ConfigNode.ObjectNode` |Represents complex structure (a subtree). Its child nodes can be of +any type. +|list |`ConfigNode.ListNode`|Represents a list of nodes. Its components can be of any type. +|value |`ConfigNode.ValueNode`| Represents a leaf node. +|=== + +A node of any type can have a `String` value. + +Each config tree in memory will have an object node as its root with +child nodes as dictated by the source config data from which the config system +built the tree. + +[NOTE] +.Missing Config Nodes +==== +If your application attempts to access a non-existent node, for example using +[source,java] +---- +config.get("key.does.not.exist") +---- +the config system returns a `Config` node object with +type `MISSING`. The in-memory config tree contains nodes only of types `OBJECT`, `LIST`, +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. +The config system supports HOCON as an +xref:supported-formats.adoc#Config-ModuleHocon[extension module]. + +[source,hocon] +.HOCON `application.conf` file +---- +app { + greeting = "Hello" + page-size = 20 + basic-range = [ -20, 20 ] +} +data { + providers: [ + { + name = "Provider1" + class = "this.is.my.Provider1" + }, + { + name = "Provider2" + class = "this.is.my.Provider2" + } + ] +} + +---- + +The diagram below illustrates the in-memory tree for that configuration. + +.Config Nodes structure of `application.conf` file +image::config/application_conf-nodes.png["Loaded Config Nodes structure",align="center"] + +==== +Notes + +1. Each non-root node has a name which distinguishes it from other nodes with +the same parent. The interpretation of the name depends on the node type. ++ +|=== +|Node Type |Name + +|object + +value |member name of the node within its parent +|list |element index of the node within the containing list +//|value |member name of the node within its parent +|=== + +2. Each node's key is the fully-qualified path using dotted names from the root to that node. +3. The root has an empty key, empty name, and no value. +==== + +The `Config` object exposes methods to return the +link:{config-javadoc-base-url}/io/helidon/config/Config.html#name--[`name`], + link:{config-javadoc-base-url}/io/helidon/config/Config.html#key--[`key`], and + link:{config-javadoc-base-url}/io/helidon/config/Config.html#type--[`type`] of the + node. + +== Access by Key [[accessByKey]] +For many applications, accessing configuration values by key will be the simplest approach. +If you write the code with a specific configuration structure in mind, your code can retrieve +the value from a specific configuration node very easily. + +Your application can specify the entire navigation path as the key to a single +`get` invocation, using dotted +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 the following lines retrieve the same `Config` node. +[source,java] +.Equivalent Config Retrievals +---- +assert config.get("") == config; +Config provName1 = config.get("data.providers.0.name"); // <1> +Config provName2 = config.get("data.providers.0").get("name"); // <2> +Config provName3 = config.get("data.providers").get("0.name"); +Config provName4 = config.get("data").get("providers.0").get("name"); +Config provName5 = config.get("data").get("providers").get("0").get("name"); // <3> +---- +<1> using a single key +<2> mixed style (composite key and single key) +<3> navigating one level with each `get` invocation + +The `Config.get(key)` method always returns a `Config` object without throwing an +exception. If the specified key does not exist the method returns a `Config` node +of type `MISSING`. There are several ways your application can tell whether a given +config value exists. + +|=== +|Method |Usage + +| `exists` |Returns `true` or `false` +| `ifExists` | Execute functional operations for present nodes +| `type` | Returns enum value for the `Config.Type`; `Config.Type.MISSING` if the node +represents a config value that _does not_ exist +| `as` | Returns the `ConfigValue` with the correct type that has all methods of `Optional` + and a few additional ones - see link:{config-javadoc-base-url}/io/helidon/config/ConfigValue.html[`ConfigValue`] interface. +|=== + +The config system throws a `MissingValueException` if the application tries to +access the value of a missing node by invoking the `ConfigValue.get()` method. + +== Access by General Navigation +Some applications might need to work with configuration without knowing its +structure or key names ahead of time, and such applications can use various +methods on the `Config` class to do this. + +.General Config Node Methods +|=== +|Method |Usage + +|`asNodeList()` |Returns a ConfigValue>. For nodes of type `OBJECT` contains child nodes as a `List`. +|`hasValue()` |For any node reports if the node has a value. This can be true for +any node type except `MISSING`. +|`isLeaf()` |Reports whether the node has no child nodes. Leaf nodes have no children +and has a single value. +|`key()` |Returns the fully-qualified path of the node using dotted notation. +|`name()` |Returns the name of the node (the last part of the key). +|`asNode()` |Returns a `ConfigValue` wrapped around the node +| `traverse()` + +`traverse(Predicate)` | Returns a `Stream` as an iterative +deepening depth-first traversal of the subtree +|`type()` |Returns the `Type` enum value for the node: `OBJECT`, `LIST`, `VALUE`, +or `MISSING` +|=== + +[source,java] +.List names of child nodes of an _object_ node +---- +List appNodeNames = config.get("app") + .asNodeList() // <1> + .map(nodes -> { // <2> + return nodes + .stream() + .map(Config::name) + .sorted() + .collect(Collectors.toList()); + }) + .orElse(Collections.emptyList()); // <3> + + +assert appNodeNames.get(0).equals("basic-range"); // <4> +assert appNodeNames.get(1).equals("greeting"); // <4> +assert appNodeNames.get(2).equals("page-size"); // <4> +---- + +<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 +<4> Check that the list contains the expected child names: `basic-range`, `greeting` and `page-size`. + +[source,java] +.List child nodes of a _list_ node +---- +List providers = config.get("data.providers") + .asNodeList().orElse(Collections.emptyList()); // <1> + +assert providers.get(0).key().toString().equals("data.providers.0"); // <2> +assert providers.get(1).key().toString().equals("data.providers.1"); // <2> +---- + +<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`. + +The `traverse()` method returns a stream of the nodes in the subtree that is rooted +at the current configuration node. +Depending on the structure of the loaded configuration the stream contains a mix of object, list or + leaf value nodes. + +[source,java] +.Traverse subtree below a _list_ node +---- +config.get("data.providers") + .traverse() // <1> + .forEach(node -> System.out.println(node.type() + " \t" + node.key())); // <2> +---- + +<1> Visit the subtree rooted at the `data.providers` _list_ node. +<2> Prints out following list of nodes (type and key): + +==== +[listing] +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. + +[source,java] +.Traverse _root_ (_object_) node, skipping the entire `data` subtree +---- +config.traverse(node -> !node.name().equals("data")) // <1> + .forEach(node -> System.out.println(node.type() + " \t" + node.key())); // <2> +---- + +<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] +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 +configuration without it knowing if or where the relevant configuration is plugged into +a larger config tree. + +For example, the xref:introduction.adoc#create-simple-config-props[`application.properties`] +from the introduction section contains several settings prefixed with `web` such as `web.page-size`. +Perhaps in another config source the same information might be stored as +`server.web.page-size`: +[source,java] +.Alternate Structure for Web Config +server.web.page-size: 40 +server.web.debug = true +server.web.ratio = 1.4 + +You might want to write the web portion of your app to work with a config subtree +with keys that are independent of the subtree's position in a larger tree. This +would allow you to reuse the web portion of your application without change, regardless +of which structure a config source used. + +One easy way to do this is to _detach_ a subtree from a larger config tree. When +your application invokes the +link:{config-javadoc-base-url}/io/helidon/config/Config.html#detach--[`Config.detach`] method it gets back +a _copy_ of the config node but with no parent. The copy and the original node both +point to the same objects for their child nodes (if any). The original node is +unchanged. +[source,java] +.Detaching a Subtree +---- +Config originalRoot = // from the original example `.conf` file +Config alternateRoot = // from the alternate structure above + +Config detachedFromOriginal = originalRoot.get("web").detach(); +Config detachedFromAlternate = alternateRoot.get("server.web").detach(); + +assert originalRoot.get("web.debug").equals("true"); // <1> +assert alternateRoot.get("server.web.debug").equals("true"); // <1> + +assert detachedFromOriginal.get("debug").equals("true"); // <2> +assert detachedFromAlternate.get("debug").equals("true"); // <2> +---- +<1> Navigation depends on knowing the full structure of the config +and so is different for the two cases. +<2> Detaching so the `web` node is the root can use the same key +regardless of where the config subtree came from. + diff --git a/docs/nima/config/introduction.adoc b/docs/nima/config/introduction.adoc new file mode 100644 index 00000000000..963076afdf7 --- /dev/null +++ b/docs/nima/config/introduction.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += The Configuration Component +:description: Helidon config introduction +:keywords: helidon, nima, config +:feature-name: Config +:rootdir: {docdir}/../.. +:imagesdir: {rootdir}/images + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/config/mutability-support.adoc b/docs/nima/config/mutability-support.adoc new file mode 100644 index 00000000000..4917f1dc659 --- /dev/null +++ b/docs/nima/config/mutability-support.adoc @@ -0,0 +1,276 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Mutability Support +:description: Helidon mutability support +:keywords: helidon, config +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> + +== Overview +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 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. + +== Using Config Metadata +=== Loading Time +The config system records when it loads each configuration into memory. +Your application can retrieve it by invoking the link:{config-javadoc-base-url}/io/helidon/config/Config.html#timestamp--[timestamp method]: +[source,java] +java.time.Instance loadTime = myConfig.timestamp(); + +on any config node. + +=== Config Context +The config system maintains a link:{config-javadoc-base-url}/io/helidon/config/Config.Context.html[`Config.Context`] +for each `Config` node. Your application can retrieve the context by invoking the `Config.context()` +method and then use it for these operations: + +.Uses of `Config.Context` +|=== +|Method |Usage + +|`Instant timestamp()` |Returns the load time of the last loaded configuration +that used the context. +|`Config last()` |Returns the most recently loaded configuration that used the context. +|`Config reload()` |Reloads the +entire config tree from the current contents of the same config sources used to +load the tree in which the current node resides. +|=== + +Note that the config context describes or replaces a currently-loaded config tree. +It by itself does not help your application decide _when_ reloading the config might be +useful. + +== Responding to Changes in Config Sources [[polling]] +[IMPORTANT] +.Evolving API +==== +This section describes the `Config.changes()` method. It is marked +as deprecated because it returns an `io.helidon.reactive.Flow.Publisher` object. +In a future Helidon release that requires Java 11 or later this method will be un-deprecated +and changed -- or a similar method will be added -- so that the return type is +`java.util.concurrent.Flow.Publisher` instead. + +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 change +in the underlying config sources by: + +1. setting up change detection for the config sources used to build a configuration, and +2. registering a response to be run when a source changes. + +Your code's response can react to the changes in whatever way makes sense +for your application. + +The following sections describe these steps in detail. + +=== Setting up Config Source Change Detection +When the application creates a config source, it can set up change detection for +that source. This is called _polling_ in the Helidon API but specific change detection +algorithms might not use actual polling. You choose a specific +link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`] for each +config source you want to monitor. See the section on +xref:extensions.adoc#Config-SPI-PollingStrategy[polling strategies] in the +config extensions doc page for more information. + +The config system provides some built-in polling strategies, exposed as these methods +on the link:{config-javadoc-base-url}/io/helidon/config/PollingStrategies.html[`PollingStrategies`] class: + +- `regular(Duration interval)` - a general-purpose scheduled polling strategy with a specified, + constant polling interval. +- `watch(Path watchedPath)` - a filesystem-specific strategy to watch + specified path. You can use this strategy with the `file` and `classpath` +built-in config sources. +- `nop()` - a no-op strategy + +This example builds a `Config` object from three sources, each set up with a +different polling strategy: + +[source,java] +.Build a `Config` with a different `PollingStrategy` for each config source +---- +Config config = Config.create( + ConfigSources.file("conf/dev.properties") + .pollingStrategy(PollingStrategies.regular(Duration.ofSeconds(2))) // <1> + .optional(), + ConfigSources.file("conf/config.properties") + .changeWatcher(FileSystemWatcher.create()) // <2> + .optional(), + ConfigSources.classpath("application.properties") + .pollingStrategy(PollingStrategies::nop)); // <3> +---- + +<1> Optional `file` source `conf/dev.properties` will be checked for changes every + `2` seconds. +<2> Optional `file` source `conf/config.properties` will be watched by the Java + `WatchService` for changes on filesystem. +<3> The `classpath` resource `application.properties` will not be checked for + changes. +`PollingStrategies.nop()` polling strategy is default. + +The polling strategies internally inform the config system when they +detect changes in the monitored config sources (except that the `nop` strategy does +nothing). + +=== Registering a Config Change Response +To know when config sources have changed, your application must register its interest +on the `Config` node of interest. The config system will then notify +your application of any change within the subtree rooted at that node. +In particular, if you register on the root node, +then the config system notifies your code of changes anywhere in the config tree. + +You can register in either of two ways: + +1. register an action to be run upon each change, or +2. subscribe to a `Flow.Publisher` that notifies of changes. + +==== Registering Actions +A simple approach is for your application to register a function that should +run when any change occurs. + +[source,java] +.Subscribe on `greeting` property changes via `onChange` method +---- +config.get("greeting") // <1> + .onChange((changedNode) -> { // <2> + System.out.println("Node " + changedNode.key() + " has changed!"); + return true; // <3> + }); +---- + +<1> Navigate to the `Config` node on which you want to register. +<2> Invoke the `onChange` method, passing a function (`Function`). +The config system invokes that function each time the subtree rooted at the +`greeting` node changes. The `changedNode` is a new instance of `Config` +representing the updated subtree rooted at `greeting`. +<3> The function should return `true` to continue being run on subsequent changes, `false` +to stop. + +==== Subscribing to Events +The config system also supports the flow publisher/subscriber model for applications +that need more control over the pace at which the config system delivers +config change events. + +Each `Config` instance exposes the link:{config-javadoc-base-url}/io/helidon/config/Config.html#changes--[`Config.changes()`] +method which returns a `Flow.Publisher`. +Your application can invoke this method, then invoke `subscribe` on the returned +`Flow.Publisher`, passing your own `Flow.Subscriber` implementation. The config system will +invoke your subscriber's methods as appropriate, most notably calling `onNext` +whenever it detects a change in one of the underlying config sources for the config +node of interest. + +Mote that your subscriber will be notified when a change occurs anywhere in the +subtree represented by the `Config` node. + +[source,java] +.Subscribe on `greeting` property changes +---- +config.get("greeting") // <1> + .changes() // <2> + .subscribe(new Flow.Subscriber<>() { // <3> + Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { // <4> + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(Config changedNode) { // <5> + System.out.println("Node " + changedNode.key() + " has changed!"); + subscription.request(1); + } + + @Override + public void onError(Throwable throwable) { // <6> + } + + @Override + public void onComplete() { // <7> + } + }); +---- + +<1> Navigate to the `Config` node on which you want to register. +<2> Invoke `changes` to get the `Flow.Publisher` of changes to the subtree rooted +at the `Config` node. +<3> Subscribe to the publisher passing a custom `Flow.Subscriber` implementation. +<4> Request the first event delivery in `onSubscribe` method. +<5> The config system invokes `onNext` each time the subtree rooted at the +`greeting` node changes. The `changedNode` is a new instance of `Config` representing +the updated subtree rooted at `greeting`, regardless of where in the subtree +the change actually occurred. Remember to request the next event delivery in `onNext`. +<6> The config system does not currently invoke `onError`. +<7> The config system invokes `onComplete` if all config sources indicate _there will + be no other change event_. + +[NOTE] +Your application _does not_ need to subscribe to the new `Config` instance passed +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 change as they happen. Instead, it's +sufficient that they simply have access to the current value for a particular +key in the configuration. + +Each `asXXX` method on the `Config` class has a companion `asXXXSupplier` method. +These supplier methods return `Supplier`, and when your application invokes +the supplier's `get` method the config system returns the _then-current value_ +as stored in the config source. + +[source,java] +.Access `greeting` property as `Supplier` +---- +// Construct a Config with the appropriate PollingStrategy on each config source. + +Supplier greetingSupplier = config.get("greeting") // <1> + .asString().supplier(); // <2> + +System.out.println("Always actual greeting value: " + greetingSupplier.get()); // <3> +---- +<1> Navigate to the `Config` node for which you want access to the always-current +value. +<2> Retrieve and store the returned supplier for later use. +<3> Invoke the supplier's `get()` method to retrieve the current value of the node. + +[IMPORTANT] +========= +Supplier support requires that you create the `Config` object from config sources that +have proper polling strategies set up. +========= diff --git a/docs/nima/config/property-mapping.adoc b/docs/nima/config/property-mapping.adoc new file mode 100644 index 00000000000..c6fb3a73d13 --- /dev/null +++ b/docs/nima/config/property-mapping.adoc @@ -0,0 +1,791 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Property Mapping +:description: Helidon config property mapping +:keywords: helidon, config +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> + +== 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`), into `enum` values, and to +express parts of the config tree as complex types (`List`, `Map`, and +custom types specific to your application). This section introduces how to +use the built-in mappings and your own custom ones to convert to simple and +complex types. + +== Converting Configuration to Simple Types +The link:{config-javadoc-base-url}/io/helidon/config/Config.html[`Config`] class itself provides many +conversions to Java types. See the JavaDoc for the complete list. + +The methods which support Java primitive types and their related classes follow a +common pattern. The examples in the table below deal with conversion to a boolean + but the same pattern applies to many data types listed in the JavaDoc. + +Assume a local variable has been assigned something like +[source,java] +---- +Config config = Config.get("someKey"); +// shortcut method +ConfigValue value = config.asBoolean(); +// generic method (for any type) +ConfigValue value2 = config.as(Boolean.class); +---- + +.Built-in Conversions to Simple Types (e.g., boolean) +|=== +|Java type |Example usage ^1^ + +|`boolean` |`boolean b = value.get();` ^2^ + +`boolean defaultedB = value.orElse(true);` ^3^ +|`Optional` | ConfigValue already has all methods of an Optional. If actual optional is needed: +`Optional b = value.asOptional();` ^4^ +|`Supplier` |`Boolean b = value.supplier().get();` + +`boolean defaultedB = value.supplier(true).get();` +|`Supplier>` | +`Boolean b = value.optionalSupplier().get().orElse(Boolean.TRUE);` +|=== + + +Notes on Built-in Conversions to Simple Types +==== +^1^ All conversions can throw `MissingValueException` (if no value exists at the +requested key and no default is provided) and +`ConfigMappingException` (if some error occurred while performing the data mapping). + +^2^ The `Config.asXXX` methods internally use the Java-provided `XXX.parseXXX` methods, so here +a missing or un-parseable string gives `false` because that is how `Boolean.parseBoolean` +behaves. + +^3^ User code defaults the value to `true`. + +^4^ User code defaults the value to `Boolean.TRUE` if absent; otherwise parses +the value using `Boolean.parseBoolean`. +==== + +The numerous conversions defined on the `Config` class for other types (integers, +doubles, etc.) will satisfy +many of your application's needs. The link:{config-javadoc-base-url}/io/helidon/config/ConfigMappers.html[`ConfigMappers`] class +includes other related mappings from `String` (rather than from `Config`) to +Java types (described in the JavaDoc). + +For additional type mapping, you can use these methods defined on `Config`: +[source,java] +T as(Class type); +T as(Function mapper); +T as(GenericType genericType); + +which maps the current node to a type. + +The next example, and later ones below showing complex type mapping, use the example +xref:introduction.adoc#built-in-formats[`application.properties`] configuration +from the config introduction. Part of that example includes this line: +[source] +---- +bl.initial-id = 10000000000 +---- +Your application can use `Config.as` to interpret the value as a `BigDecimal`: +[source,java] +BigDecimal initialId = config.get("bl.initial-id").as(BigDecimal.class); + +== Converting Configuration to `enum` Values +Configuration can automatically map `Config` nodes to most `enum` types. + +Your application code simply passes the enum class type to `config.as(Class type)`. +The built-in `enum` converter attempts to match the string value in the config node to the name of one of the values declared for that specific `enum` in the Java code. + +=== Matching `enum` Names +The conversion applies the following algorithm to match config values to `enum` names, stopping as soon as it finds a match: + +1. Select an exact match if one exists. +2. Treat hyphens (`-`) in config strings as underscores (`_`) and select an otherwise exact match if one exists. +3. Select a _case-insensitive_ match (with or without hyphen substitution) if there is _exactly one_ such match. +4. Finding no match or multiple case-insensitive matches, throw a `ConfigMappingException`. + +=== Example +The following example illustrates how to use the built-in `enum` conversion feature. The example code builds a simple `Config` tree itself which contains simple test data; normally your application would load the config from a file or some other location. + +[source,java] +---- +class Example { + + enum Color {RED, YELLOW, BLUE_GREEN}; + + void convert() { + Config config = Config.just(ConfigSources.create(Map.of("house.tint", "blue-green", + "car.color", "Red", + "warning", "YELLOW"))); + + Color house = config.get("house.tint") // <1> + .as(Color.class) // <2> + .get(); // <3> + Color car = config.get("car.color") + .as(Color.class) + .get(); // <4> + Color warning = config.get("warning") + .as(Color.class) + .get(); // <5> + } +} +---- +<1> Retrieve the `Config` object corresponding to the key `house.tint`. +<2> Indicate that, when the value in that `Config` object is converted, Helidon should convert it to a `Color` `enum` value. +<3> Convert and retrieve the value. ++ +The conversion triggered by invoking `get()` matches the string `blue-green`--expressed in lower case and with a hyphen -- to `Color.BLUE_GREEN` using the conversion rules described earlier. +<4> The config key `car.color` locates the mixed-case string `Red` which the converter matches to `Color.RED`. +<5> The config key `warning` locates `YELLOW` which the converter matches exactly to `Color.YELLOW`. + +=== Why use heuristics in matching strings to `enum` values? +Short answer: ease-of-use. + +Users composing config sources often adopt a style with hyphens within words to improve readability and lower-case keys and values. +With that style in mind, users typing an `enum` value into a config source might accidentally enter a hyphen instead of an underscore or use lower case instead of upper case. Users might even _prefer_ to make these changes so they can follow their preferred config style. + +With the heuristics, Helidon allows users to adopt a common config style and prevents unnecessary runtime exceptions--and user frustration--from inconsequential typos. + +Remember: + +* Helidon always finds exact matches unambiguously, without relying on the heuristics. +In our `Color` example the text `BLUE_GREEN` always maps to `Color.BLUE_GREEN`. +* Because hyphens cannot appear in a valid Java `enum` value name, interpreting them as underscores during `enum` conversion introduces no ambiguity. + +Only in the following unusual sitatuation are the heuristics unable to unambiguously match a string to an `enum` value: + +* The `enum` has values which differ _only_ in their case (such as `Red` and `RED`), _and_ +* The string in the config source is not an exact match with an `enum` value name (such as `red`). + +If your application must deal with such cases, write your own function which maps a `Config` node to the correct `enum` value, resolving the ambiguities however makes sense in your use case. +Your code tells config to use that function instead of the built-in `enum` conversion when it converts values. A xref:customConfigAs[later section] describes this technique which works for all types, not only `enum` types. + +== Converting Configuration to Complex Types + +The xref:hierarchical-features.adoc[hierarchical features] section describes +the tree structure used to represent config data. The config system can map subtrees +of a config tree to complex Java types. + +=== Built-in Conversions to `List` and `Map` +The `Config` class exposes several methods for mapping a structured config node +to a Java `List` or `Map`. The link:{config-javadoc-base-url}/io/helidon/config/Config.html[JavaDoc] +contains complete details, but briefly your application can convert a structured `Config` node into: + +* a `List` of a given type +* a `Map` in which each key is the fully-qualified key `String` for a +config entry and the value is its `String` value + +=== Custom Conversions +Often your code will be simpler if you can treat parts of the configuration as +custom, application-specific Java objects, rather than as a group of `String` keys and +values. You will need customized conversions to do so. + +The config system provides many ways to accomplish this, described in +the link:{config-javadoc-base-url}/io/helidon/config/package-summary.html#conversions[`io.helidon.config` +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 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 +to the target class. For both approaches, you write your own conversion function. +The difference is in how your application triggers the use of that mapper. + +==== Use Custom Mapper Explicitly: `Config.as` method +Any time your application has a `Config` instance to map to the target class +it invokes `Config.as` passing an instance of the corresponding conversion function: +[source,java] +---- +Config config = Config.get("web"); +ConfigValue web = config.as(WebConfigMapper::map); +---- +You do not necessarily need a new instance of the mapper every time you want to use +it. + +In this approach, everywhere your application needs to perform this conversion it specifies the +mapper to use. If you decided to change which mapper to use you would need +to update each of those places in your application. + +==== Register Custom Mapper Once, Use Implicitly: `Config.as` method +In this approach, your application: + +1. Tells each `Config.Builder` +that needs to know about the custom mapper by either: + a. registering an instance of your mapper by invoking `Config.Builder.addMapper`, or + b. implementing +link:{config-javadoc-base-url}/io/helidon/config/spi/ConfigMapperProvider.html[`ConfigMapperProvider`] +so it returns an instance of your mapper (see the JavaDoc for complete information) +and creating or editing the file `io.helidon.config.spi.ConfigMapperProvider` +so it contains +a line with the fully-qualified class name of your `ConfigMapperProvider`. The +config system will use the Java service loader to find and invoke all +`ConfigMapperProvider` classes listed and add the mappers they provide to each +`Config.Builder` automatically. +2. Converts using the mapper by invoking the +`Config.as` method which accepts the target type to convert to, _not_ the +mapper itself that does the conversion. + +If your application converts to the same +target type in several places in the code, this approach allows you to change which mapper it uses by +changing only the _registration_ of the mapper, not each use of it. + +[[WebConfig]] +==== Continuing the `Web` Example +The following examples build on the example configuration from the +xref:introduction.adoc#built-in-formats[`application.properties`] +example file in the introduction. + + +[source,java] +.Java POJO to Hold `web` Properties Config +---- +public class WebConfig { + private boolean debug; + private int pageSize; + private double ratio; + + public WebConfig(boolean debug, int pageSize, double ratio) { + this.debug = debug; + this.pageSize = pageSize; + this.ratio = ratio; + } + + public boolean isDebug() { + return debug; + } + + public int getPageSize() { + return pageSize; + } + + public double getRatio() { + return ratio; + } +} +---- + +[source,java] +.Custom Mapper Class +---- +public class WebConfigMapper implements Function { + + @Override + public WebConfig apply(Config config) { + return new WebConfig( + config.get("debug").asBoolean().orElse(false), + config.get("page-size").asInt().orElse(10), + config.get("ratio").asDouble().orElse(1.0) + ); + } + } +---- + +[source,java] +.Explicitly Using the Mapper +---- +Config config = Config.create(classpath("application.properties")); + +WebConfig web = config.get("web") + .as(new WebConfigMapper()) + .get(); +---- + +[source,java] +.Registering and Implicitly Using the Mapper +---- +Config config = Config.builder(classpath("application.properties")) + .addMapper(WebConfig.class, new WebConfigMapper()) + .build(); + +WebConfig web = config.get("web") + .as(WebConfig.class) + .get(); +---- +Either of the two approaches just described will _always_ work without requiring you to change +the POJO class. + +[[customConfigAs]] +== Advanced Conversions using Explicit Mapping Logic +If the target Java class you want to use meets certain conditions -- or if you can change +it to meet one of those conditions -- you might not need to write a separate mapper +class. Instead, you add the mapping logic to the POJO itself in one of +several ways and the config system +uses Java reflection to search for those ways to perform the mapping. + +Your application facilitates this implicit mapping either by adding to the +POJO class or by providing a builder class for it. + +This feature is available in Object mapping module, and is added through Java `ServiceLoader` +mechanism. This is no longer part of core Config module, as it depends on reflection +and introduces a lot of magic (see the list of supported mapping methods below, also +uses reflection to invoke the methods and to map configuration values to fields/methods etc.). + +[source,xml] +.Config object mapping Dependency in `pom.xml` +---- + + + io.helidon.config + helidon-config-object-mapping + + +---- + +=== Adding the Mapping to the POJO +If you can change the target class you can add any one of the following methods or +constructors to the POJO class which the config system will find and use for mapping. + +Continuing with the <> example introduced earlier: + +.Methods Supporting Auto-mapping +|=== + +|`static WebConfig create(Config);` +|`static WebConfig from(Config);` +|`static WebConfig from(String);` +|`static WebConfig of(Config);` +|`static WebConfig of(String);` +|`static WebConfig valueOf(Config);` +|`static WebConfig valueOf(String);` +|`static WebConfig fromConfig(Config);` +|`static WebConfig fromString(String);` +|=== + +.Constructors Supporting Auto-mapping +|=== + +|`WebConfig(Config);` +|`WebConfig(String);` +|=== + +If the config system finds any of these methods or constructors when the +application invokes + +[source,java] +WebConfig wc = config.as(WebConfig.class).get(); + +it will invoke the one it found to +map the config data to a new instance of the target class. You do not need to +write a separate class to do the mapping or register it with the `Config.Builder` +for the config instance. + + + +=== Writing a Builder Method and Class for the POJO +You can limit the changes to the POJO class by adding a single +`builder` method to the POJO which returns a builder class for the POJO: +[source,java] +---- +public class WebConfig { + static WebConfigBuilder builder() { + return new WebConfigBuilder(); + } +} +---- + +The builder class `WebConfigBuilder` is expected to be a Java Bean with + +1. bean properties named for the config properties of interest, and +2. a method `WebConfig build()` which creates the mapped instance +from the builder's own bean properties. + +When your application invokes `config.as(WebConfig.class)` the config system + +1. finds and invokes the `WebConfig.builder()` method, +2. assigns the bean properties on the returned builder from the config subtree +rooted at `config`, and +3. invokes the builder's `build()` method yielding the resulting `WebConfig` instance. + + +== Conversions using JavaBean Deserialization +//// +10. a factory method `from(...)` with parameters (loaded from config sub-nodes) + creates new instance of a bean. +11. a _factory_ constructor with parameters (loaded from config sub-nodes). +12. a no-parameter constructor to create new instance of type and apply + recursively same mapping behaviour +described above on each JavaBean property of such object, +a.k.a. <>. +//// + +The config system can also interpret your classes as JavaBeans and use +the normal bean naming conventions to map configuration data to your POJO classes, +using one of these patterns: + +1. <> - The config system treats the target class itself as +a JavaBean, assigning values from the config to the bean properties of the POJO +class. +2. <> - The config system invokes the POJO's `builder()` +method to obtain a builder for that POJO type and treats the _builder_ +class as a JavaBean, assigning values from the config to the builder's +bean properties and then invoking the builder's `build` method to create +an instance of the target POJO class. +3. <> - The +config system finds a `from` method or a constructor on +the POJO class itself which accepts annotated arguments, then invokes that method +or constructor +passing the specified arguments based on the config. The `from` method returns +an instance of the POJO class initialized with the values passed as arguments. + +The following sections describe these patterns in more detail. + +This feature is available in Object mapping module, and is added through Java `ServiceLoader` +mechanism. This is no longer part of core Config module, as it depends on reflection. + +[source,xml] +.Config object mapping Dependency in `pom.xml` +---- + + + io.helidon.config + helidon-config-object-mapping + + +---- + +=== POJO as JavaBean [[pojoAsJavaBean]] +If your POJO target class is already a JavaBean -- or you can modify it +to become one -- you might be able to avoid writing any explicit +mapping code yourself. + +The config system invokes the no-args constructor on the target class to create +a new instance. It treats each public setter method and each public non-final field +as a JavaBean property. The config system processes any non-primitive property +recursively as a JavaBean. In this way the config system builds up the target +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 JavaBean processing for a given property. + +.`Value` Annotation +|=== +|Attribute |Usage + +|`key` |Indicates which config key should match this JavaBean property +|`withDefault` |`String` used for the bean property default value if none is set in the config +|`withDefaultSupplier` |`Supplier` of the default bean property value if not is set in the config +|=== + +To exclude a bean property from the config system bean processing annotate it with +link:{config-javadoc-base-url}/io/helidon/config/Config.Transient.html[`Config.Transient`]. + +Here is an example using the `app` portion of the example configuration from the +introduction. + +[source,java] +.Java bean to load `app` properties into via setters +---- +public class AppConfig { + private Instant timestamp; + private String greeting; + private int pageSize; + private List basicRange; + + public AppConfig() { // <1> + } + + public void setGreeting(String greeting) { // <2> + this.greeting = greeting; + } + public String getGreeting() { + return greeting; + } + + @Value(key = "page-size", // <3> + withDefault = "10") // <4> + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + public int getPageSize() { + return pageSize; + } + + @Value(key = "basic-range", // <5> + withDefaultSupplier = BasicRangeSupplier.class) // <6> + public void setBasicRange(List basicRange) { + this.basicRange = basicRange; + } + public List getBasicRange() { + return basicRange; + } + + @Config.Transient // <7> + public void setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + } + public Instant getTimestamp() { + return timestamp; + } + + public static class BasicRangeSupplier + implements Supplier> { // <8> + @Override + public List get() { + return List.of(-10, 10); + } + } +} +---- + +<1> Public no-parameter constructor. +<2> Property `greeting` is not customized and will be set from the config node with + the key `greeting`, if present in the config. +<3> Property `pageSize` is matched to the config key `page-size`. +<4> If the `page-size` config node does not exist, the `pageSize` bean property defaults to `10`. +<5> Property `basicRange` is matched to the config key `basic-range`. +<6> If the `basic-range` config node does not exist, a `BasicRangeSupplier` instance will provide +the default value. +<7> The `timestamp` bean property is never set, even if the config contains a node + with the key `timestamp`. +<8> `BasicRangeSupplier` is used to supply the `List` default value. + +Here is an example of code loading config and mapping part of it to the `AppConfig` +bean above. + +[source,java] +.Map `app` config node into `AppConfig` class +---- +Config config = Config.create(classpath("application.conf")); + +AppConfig app = config.get("app") + .as(AppConfig.class) + .get(); // <1> + +//assert that all values are loaded from file +assert app.getGreeting().equals("Hello"); +assert app.getPageSize() == 20; +assert app.getBasicRange().size() == 2 + && app.getBasicRange().get(0) == -20 + && app.getBasicRange().get(1) == 20; + +//assert that Transient property is not set +assert app.getTimestamp() == null; // <2> +---- + +<1> The config system finds no registered `ConfigMapper` for `AppConfig` and so +applies the JavaBean pattern to convert the config to an `AppConfig` instance. +<2> Because the bean property `timestamp` was marked as transient, the +config system did not set it. + + +=== Builder as JavaBean [[builderAsJavaBean]] +If the target class includes the public static method `builder()` that returns any object, +then the config system will make sure that the return type has a method `build()` +which returns an instance of the target class. If so, the config system treats +the _builder_ as a JavaBean and + +1. invokes the `builder()` method to instantiate the builder class, +2. treats the _builder_ as a JavaBean and maps the `Config` subtree to it, +3. invokes the builder's `build()` method to create the new instance of the target +class. + +You can augment the target class with the public static `builder()` method: + +[source,java] +.JavaBean for `app` properties, via a `Builder` +---- +public class AppConfig { + private String greeting; + private int pageSize; + private List basicRange; + + private AppConfig(String greeting, int pageSize, List basicRange) { // <1> + this.greeting = greeting; + this.pageSize = pageSize; + this.basicRange = basicRange; + } + + public String getGreeting() { + return greeting; + } + + public int getPageSize() { + return pageSize; + } + + public List getBasicRange() { + return basicRange; + } + + public static Builder builder() { // <2> + return new Builder(); + } + + public static class Builder { // <3> + private String greeting; + private int pageSize; + private List basicRange; + + private Builder() { + } + + public void setGreeting(String greeting) { // <4> + this.greeting = greeting; + } + + @Value(key = "page-size", + withDefault = "10") + public void setPageSize(int pageSize) { // <5> + this.pageSize = pageSize; + } + + @Value(key = "basic-range", + withDefaultSupplier = BasicRangeSupplier.class) + public void setBasicRange(List basicRange) { // <6> + this.basicRange = basicRange; + } + + public AppConfig build() { // <7> + return new AppConfig(greeting, pageSize, basicRange); + } + } +} +---- + +<1> The target class's constructor can be `private` in this case because new instances are created +from the inner class `Builder` which has access to `AppConfig`'s private members. +<2> The target class contains `public static` method `builder()` which returns +an object that itself exposes the method `AppConfig build()`, so the config system +recognizes it. +<3> The config system treats the `AppConfig.Builder` (not the enclosing +target class) as a JavaBean. +<4> The builder's property `greeting` is not customized and is set from config node with + `greeting` key, if one exists. +<5> The builder's property `pageSize` maps to the config key `page-size` and +defaults to `10` if absent. +<6> The builder's property `basicRange` maps to the config key `basic-range` +and uses a `BasicRangeSupplier` instance to get a default value if needed. +<7> Finally, the config system invokes the builder's public method `build()`, +creating the new instance of `AppConfig` for use by the application. + +=== Target Class with Annotated Factory Method or Constructor [[pojoWithFactoryMethodOrConstructor]] + +Another option is to annotate the parameters to a _factory method_ or to a constructor +on the target class. You can add a _factory method_ to the target class, a `public static` +method `from` with parameters annotated to link them to the corresponding config +keys. Or you can add or modify a constructor with parameters, similarly annotated +to form the link from each parameter to the corresponding config key. + +[WARNING] +========= +Be sure to annotate each parameter of the `from` method or constructor with `@Value` +and specify the key to use for the mapping. The parameter names in the Java code +are not always available at runtime to map to config keys. (They might be `arg0`, + `arg1`, etc.) +========= + +[source,java] +.Target Class with Factory Method `from` +---- +public class AppConfig { + private final String greeting; + private final int pageSize; + private final List basicRange; + + private AppConfig(String greeting, int pageSize, List basicRange) { // <1> + this.greeting = greeting; + this.pageSize = pageSize; + this.basicRange = basicRange; + } + + public String getGreeting() { + return greeting; + } + + public int getPageSize() { + return pageSize; + } + + public List getBasicRange() { + return basicRange; + } + + public static AppConfig from( // <2> + @Value(key = "greeting") + String greeting, // <3> + @Value(key = "page-size", + withDefault = "10") + int pageSize, + @Value(key = "basic-range", + withDefaultSupplier = BasicRangeSupplier.class) + List basicRange) { + return new AppConfig(greeting, pageSize, basicRange); + } +} +---- + +<1> The target class constructor can be `private` because the factory method on +the same class has access to it. +<2> The config system invokes the factory method `from(...)`, passing +arguments it has fetched from the correspondingly-named config subtrees. +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`. + +Alternatively, you can use an annotated constructor instead of a static factory +method. Revising the example above, make the constructor public, annotate its +parameters, and remove the now-unneeded `from` factory method. + +[source,java] +.Target Class with Annotated Public Constructor +---- +public class AppConfig { + public AppConfig( // <1> + @Value(key = "greeting") // <2> + String greeting, + @Value(key = "page-size", + withDefault = "10") + int pageSize, + @Value(key = "basic-range", + withDefaultSupplier = BasicRangeSupplier.class) + List basicRange) { + this.greeting = greeting; + this.pageSize = pageSize; + this.basicRange = basicRange; + } +} +---- +<1> Constructor is `public`. +<2> Each parameter has the `ConfigValue` annotation to at least specify the +config key name. + +When the application invokes `config.as(AppConfig.class)`, the config system locates +the public annotated constructor and invokes it, passing as arguments the data it fetches +from the configuration matching the annotation `key` names with the configuration +keys. diff --git a/docs/nima/config/supported-formats.adoc b/docs/nima/config/supported-formats.adoc new file mode 100644 index 00000000000..0c45750c244 --- /dev/null +++ b/docs/nima/config/supported-formats.adoc @@ -0,0 +1,453 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Additional Supported Formats and Sources +:description: Helidon config supported formats and sources +:keywords: helidon, config +:feature-name: Config +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== Contents + +- <> +- <> +- <> + +== Overview +Helidon Config provides several extension modules that support other configuration +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 + +=== Automatic Media Type and File Type Handling +With each of the parsers described here, your application can either + +1. explicitly add a parser of the correct implementation to the `Config.Builder`, or +2. rely on Java service loading and the config system's matching of file types and +media types to parsers. + +If your application creates a `Config.Builder` with parser services _disabled_ +(see link:{config-javadoc-base-url}/io/helidon/config/Config.Builder.html#disableParserServices--[`disableParserServices`] +then that builder will not find the Java services for the various parsers and so +will be unable to match the file type or media type of sources with the corresponding +parser automatically. So if you want to use automatic type +matching with a given builder, do not invoke `Config.Builder.disableParserServices()`. + +=== YAML [[Config-ModuleYaml]] + +==== Maven Coordinates + +Add the following dependency in your project: + +[source,xml] +.Config YAML Dependency in `pom.xml` +---- + + io.helidon.config + helidon-config-yaml + +---- + +[source,java] +.Config YAML Dependency in `module-info.java` +---- +module myModule { + requires io.helidon.config.yaml; +} +---- + +==== Using the YAML Parser +The YAML parser handles the following media type: + +- `application/x-yaml` - YAML format (file type `.yaml`) + +[source,java] +.Automatic selection +---- +Config config = Config.create(classpath("application.yaml")); // <1> +---- + +<1> The config system automatically maps the file type `.yaml` to the media type `application/x-yaml` +which the Helidon YAML parser matches. + +[source,java] +.YAML parser specified - no file type on source +---- +Config config = Config.create(classpath("my-config") // <1> + .parser(YamlConfigParserBuilder.buildDefault())); // <2> +---- + +<1> The media type of the source `my-config` is unknown, so the config system +cannot choose a parser automatically. +<2> The config system will parse the resource `my-config` on the runtime classpath +using the YAML parser instance created by the +link:{config-javadoc-base-url}/io/helidon/config/yaml/YamlConfigParserBuilder.html[`YamlConfigParserBuilder`]. +The `buildDefault()` method creates a config parser with default behavior. + +[source,java] +.Media type specified +---- +Config config = Config.create(classpath("my-config") // <1> + .mediaType("application/x-yaml")); // <2> +---- + +<1> The media type of the source `my-config` is unknown, so the config system +cannot choose a parser automatically. +<2> Specifying the media type for the config source allows the config system to +use its matching algorithm with the available parsers to choose a parser for that type. + +[source,java] +.YAML parser specified because parser services disabled +---- +Config config = Config.builder(classpath("application.yaml")) + .disableParserServices() // <1> + .addParser(YamlConfigParserBuilder.buildDefault()) // <2> + .build(); +---- + +<1> Disables automatic parser lookup and registration. +<2> Explicit registration of the YAML parser is therefore required. + +=== HOCON/JSON [[Config-ModuleHocon]] +The Helidon HOCON config module handles sources in the +HOCON and JSON formats. + +==== Maven Coordinates +Add the following dependency in your project: + +[source,xml] +.Config HOCON Dependency in `pom.xml` +---- + + io.helidon.config + helidon-config-hocon + +---- + +[source,java] +.Config HOCON Dependency in `module-info.java` +---- +module myModule { + requires io.helidon.config.hocon; +} +---- + +==== Using the HOCON/JSON Parser + +The parser handles the following media types: + +- `application/hocon` - HOCON format (file type `.conf`) +- `application/json` - JSON format (file type `.json`) + +[source,java] +.Automatic selection +---- +Config config = Config.create(classpath("application.conf")); // <1> +---- + +<1> The config system automatically maps the file type `.conf` to the media type `application/hocon +which the Helidon HOCON parser matches. + +The same module and parser supports file type `.json` and the media type + `application/json`. + +[source,java] +.HOCON parser specified - no file type on source +---- +Config config = Config.create(classpath("my-config") // <1> + .parser(HoconConfigParser.create())); // <2> +---- + +<1> the media type of the source `my-config` is unknown, so the config system cannot +choose a parser automatically. +<2> The config system will parse the resource `my-config` using the HOCON parser created +by the link:{config-javadoc-base-url}.hocon/io/helidon/config/hocon/HoconConfigParser.html[HoconConfigParser]. +The `create()` method creates a config parser with default behavior. + +[source,java] +.Media type specified +---- +Config config = Config.create(classpath("my-config") // <1> + .mediaType("application/hocon")); // <2> +---- + +<1> The media type of the source `my-config` is unknown, so the config system +cannot choose a parser automatically. +<2> Specifying the media type for the config source allows the config system to +use its matching algorithm with the available parsers to choose a parser for that +type. + +[source,java] +.HOCON parser specified because parser services disabled +---- +Config config = Config.builder(classpath("application.conf")) + .disableParserServices() // <1> + .addParser(HoconConfigParser.create()) // <2> + .build(); +---- + +<1> Disables automatic parser lookup and registration. +<2> Explicit registration of the HOCON parser is therefore required. + +[source,java] +.Customized HOCON parser +---- +Config config = Config.builder(classpath("application.conf")) + .disableParserServices() + .addParser(HoconConfigParser.builder() // <1> + .resolvingEnabled(false) // <2> + .build()) // <3> + .build(); +---- + +<1> Creates new instance of the parser builder. +<2> Disables resolution of substitutions. +(See the link:https://github.com/lightbend/config/blob/master/HOCON.md#substitutions[HOCON documentation].) +<3> Builds a new instance of the HOCON config parser. + +You can also specify +link:https://github.com/lightbend/config/blob/master/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java[`ConfigResolveOptions`] +using the `HoconConfigParser.builder().resolveOptions` method. + +== Additional Config Source Types + +=== Etcd [[Config-ModuleEtcd]] + +The Helidon Etcd config module supports reading configuration from a specified + Etcd key. + +==== Maven Coordinates +Add the following dependency to your project: +[source,xml] +.Config Etcd Dependency in `pom.xml` +---- + + io.helidon.config + helidon-config-etcd + +---- + +[source,java] +.Config Etcd Dependency in `module-info.java` +---- +module myModule { + requires io.helidon.config.etcd; +} +---- + +==== Using the Etcd Config Source +To read configuration from an Etcd source, your application uses the +link:{config-javadoc-base-url}/io/helidon/config/etcd/EtcdConfigSourceBuilder.html[`EtcdConfigSourceBuilder`]. + +[source,java] +.Use Etcd config source +---- +Config config = Config.create( + EtcdConfigSourceBuilder // <1> + .create(URI.create("http://my-etcd:2379"), // <2> + "/config.yaml", // <3> + EtcdConfigSourceBuilder.EtcdApi.v3)); // <4> +---- + +<1> Use the factory method `EtcdConfigSourceBuilder.create` to initialize the builder. +<2> Specify the Etcd endpoint address. +<3> Specify the Etcd key of the configuration document. +<4> Version of the Etcd API to use; `v2` and `v3` are supported. + +The config system will use the <> automatically in this example +because the file type of the key is `.yaml`. + +The `EtcdConfigSourceBuilder` class extends +link:{config-javadoc-base-url}/io/helidon/config/spi/AbstractParsableConfigSource.Builder.html[`AbstractParsableConfigSource.Builder`] +and so supports the usual settings on config sources. + +==== Monitoring for Source Changes +The Etcd support includes a polling strategy designed for an etcd config source. + +[source,java] +.Use Etcd config source +---- +Config config = Config.create( + EtcdConfigSourceBuilder + .create(URI.create("http://my-etcd:2379"), "/config.yaml", EtcdApi.v3) + .pollingStrategy(EtcdWatchPollingStrategy::new)); // <1> +---- + +<1> Use the etcd-specific polling strategy. + + +==== Loading Meta-configuration via Etcd +The config system can load information about config sources from +xref:advanced-configuration.adoc#Config-Advanced-Config-MetaConfig[meta-configuration] +rather than requiring your application to construct the builder. To read +meta-configuration from an Etcd source set the following required properties +for the source: + +* `type` to `etcd`, or `class` to `io.helidon.config.etcd.EtcdConfigSourceBuilder` +* `uri` (type `URI`) - Etcd endpoint URI. +* `key` (type `String`) - Etcd key that is associated with the configuration. +* `api` (type `EtcdConfigSourceBuilder.EtcdApi`, i.e. `v2` or `v3`) - Etcd API + version. + +Other optional `properties` are inherited from + `AbstractParsableConfigSource.Builder`. (see +link:{config-javadoc-base-url}/io/helidon/config/spi/AbstractParsableConfigSource.Builder.html#init-io.helidon.config.Config-[javadoc]) + +[source,java] +.Load Config from meta-configuration +---- +Config config = Config.loadSourcesFrom(classpath("config-meta-etcd.yaml")); +---- + +[source,YAML] +.Meta-config `config-meta-etcd.yaml` for the etcd source +---- +sources: + - type: "etcd" # <1> + properties: + uri: "http://my-etcd:2379" # <2> + key: "/config.yaml" # <2> + api: "v3" # <2> + polling-strategy: + class: "io.helidon.config.etcd.EtcdWatchPollingStrategy" # <3> + +---- + +<1> `etcd` config source type +<2> Etcd source-specific (mandatory) `properties`: `uri`, `key` and `api`. +<3> Polling strategy `EtcdWatchPollingStrategy` is automatically initialized by + specified mandatory `properties`. + +=== git [[Config-ModuleGit]] +The Helidon git config module supports reading configuration from a git + repository. + +==== Maven Coordinates +Add the following dependency to your project: +[source,xml] +.Config git Dependency in `pom.xml` +---- + + io.helidon.config + helidon-config-git + +---- + +[source,java] +.Config git Dependency in `module-info.java` +---- +module myModule { + requires io.helidon.config.git; +} +---- + +==== Using the git Config Source +To read configuration from a git source, your application uses the +link:{config-javadoc-base-url}/io/helidon/config/git/GitConfigSourceBuilder.html[`GitConfigSourceBuilder`]. + +[source,java] +.Use git config source +---- +Config config = Config.create( + GitConfigSourceBuilder + .create("application.conf") // <1> + .uri(URI.create("https://github.com/okosatka/test-config.git")) // <2> + .directory(Paths.get("/config")) // <3> + .branch("dev")); // <4> +---- + +<1> Use the factory method `GitConfigSourceBuilder.create` to initialize the builder + with a mandatory path to the configuration file. +<2> Specify the git repository URI. +<3> Specify a directory where the git repository is already cloned or it will be cloned. +<4> Specify the git branch. + +Note that the config system will use the <> in +this example because the file type is `.conf`. Recall that for this to work the +HOCON config module must be on module-path or classpath. + +The `GitConfigSourceBuilder` supports the usual source builder properties because + it extends +link:{config-javadoc-base-url}/io/helidon/config/spi/AbstractParsableConfigSource.Builder.html[`AbstractParsableConfigSource.Builder`]. + +==== Monitoring for Source Changes +Your application can monitor changes to a configuration loaded from a git source +associating the `regular` built-in polling strategy with the source. +[source,java] +.Use of git config source with polling strategy +---- +Config config = Config.create( + GitConfigSourceBuilder + .create("application.conf") + .uri(URI.create("https://github.com/okosatka/test-config.git")) + .pollingStrategy(PollingStrategies.regular(Duration.ofMinutes(5)))); // <1> +---- +<1> Use `PollingStrategies.regular(Duration duration)` to monitor for config changes. + +You can also implement your own polling strategy by implementing +link:{config-javadoc-base-url}/io/helidon/config/spi/PollingStrategy.html[`PollingStrategy`]. See +the xref:mutability-support.adoc[mutability support] and +xref:extensions.adoc#Config-SPI-PollingStrategy[polling strategy] discussions. + +==== Loading Meta-configuration via git + +The config system can load information about config sources from meta-configuration +rather than requiring your application to construct the builder. To read +meta-configuration from a git config source set the following properties for the source: + +* `type` to `git` or `class` to `io.helidon.config.git.GitConfigSourceBuilder` +* `path` (type `String`) - Relative path to the configuration file in repository. +* `uri` (type `URI`) - URI to the git repository. +* `directory` (type `Path`) - Directory with a cloned repository, by default + a temporary directory. +* `branch` (type `String`) - git branch (default is `master`). + +The meta-configuration must set the `path` and one of `uri` or `directory`. +Other optional `properties` are inherited from + `AbstractParsableConfigSource.Builder` (see + link:{config-javadoc-base-url}/io/helidon/config/spi/AbstractParsableConfigSource.Builder.html#init-io.helidon.config.Config-[javadoc]) + +[source,java] +.Load Config from meta-configuration +---- +Config config = Config.loadSourcesFrom(classpath("config-meta-git.yaml")); +---- + +[source,YAML] +.Meta-config `config-meta-git.yaml` for the git source +---- +sources: + - type: "git" # <1> + properties: + path: "application.conf" # <2> + uri: "https://github.com/okosatka/test-config.git" # <2> + directory: "/config" # <2> + branch: "dev" # <2> + polling-strategy: + type: "regular" # <3> + properties: + interval: "PT5M" # <3> + +---- + +<1> `git` config source type +<2> git source-specific properties: `path`, `uri`, `directory` and `branch`. +<3> Polling strategy `regular` with an interval, in + `Duration` format, of 5 minutes in this example. diff --git a/docs/nima/cors.adoc b/docs/nima/cors.adoc new file mode 100644 index 00000000000..247fb3f06e8 --- /dev/null +++ b/docs/nima/cors.adoc @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += CORS in Helidon Nima +:toc: +:toc-placement: preamble +:description: Helidon Nima CORS Support +:keywords: helidon, java, cors, nima, configuration, services +:feature-name: CORS +:cors-config-key-explanation: , identified by a configuration key of your choosing, +:mapped-config-top-key: my-cors +:config-table-methods-column-header: builder method +:!cors-config-table-exclude-keys: +:basic-table-intro: The table below lists the configuration keys that identify the CORS characteristics. +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/dbclient.adoc b/docs/nima/dbclient.adoc new file mode 100644 index 00000000000..51f266f9ba0 --- /dev/null +++ b/docs/nima/dbclient.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += About Helidon DB Client +:description: Helidon DB Client +:keywords: helidon, nima, database, dbclient +:feature-name: DB Client +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/fault-tolerance.adoc b/docs/nima/fault-tolerance.adoc new file mode 100644 index 00000000000..876ac8ee7ee --- /dev/null +++ b/docs/nima/fault-tolerance.adoc @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Fault Tolerance in Helidon Nima +:toc: +:toc-placement: preamble +:description: Fault Tolerance in Helidon Nima +:keywords: helidon, java, fault, tolerance, fault tolerance, nima +:feature-name: Fault Tolerance +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/graphql.adoc b/docs/nima/graphql.adoc new file mode 100644 index 00000000000..8ee76416a08 --- /dev/null +++ b/docs/nima/graphql.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += GraphQL Server Introduction +:description: Helidon GraphQL Server Introduction +:keywords: helidon, graphql, java +:feature-name: GraphQL +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/grpc/client.adoc b/docs/nima/grpc/client.adoc new file mode 100644 index 00000000000..115164fa02c --- /dev/null +++ b/docs/nima/grpc/client.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += gRPC Client +:description: Helidon gRPC Client +:keywords: helidon, grpc, java, nima +:feature-name: gRPC Client +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/grpc/server.adoc b/docs/nima/grpc/server.adoc new file mode 100644 index 00000000000..c4ab0ff6855 --- /dev/null +++ b/docs/nima/grpc/server.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += gRPC Server +:description: Helidon gRPC Server +:keywords: helidon, grpc, java, nima +:feature-name: gRPC Server +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/guides/config.adoc b/docs/nima/guides/config.adoc new file mode 100644 index 00000000000..1628cfacf43 --- /dev/null +++ b/docs/nima/guides/config.adoc @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima Config Guide +:description: Helidon configuration +:keywords: helidon, configuration, microprofile, guide, Nima +:toc: +:rootdir: {docdir}/../.. +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to create a sample Helidon Nima project that can be used to run some +basic examples using both default and custom configuration. + +== What you need + +For this 20 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + diff --git a/docs/nima/guides/dbclient.adoc b/docs/nima/guides/dbclient.adoc new file mode 100644 index 00000000000..39ed8342d84 --- /dev/null +++ b/docs/nima/guides/dbclient.adoc @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima DB Client Guide +:description: Helidon db-client +:keywords: helidon, db, client +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes Helidon DB Client features and how to create a sample Helidon Nima project +that can be used to run some basic example using Helidon database client. + +== What You Need + +For this 15 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + diff --git a/docs/nima/guides/graalnative.adoc b/docs/nima/guides/graalnative.adoc new file mode 100644 index 00000000000..f0bcd8a0449 --- /dev/null +++ b/docs/nima/guides/graalnative.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += GraalVM Native Images +:description: Helidon Native Image +:keywords: helidon, guide, graalvm, native-image +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to build a GraalVM native image for a Helidon Nima application. + +include::{rootdir}/includes/guides/graalnative.adoc[] diff --git a/docs/nima/guides/gradle-build.adoc b/docs/nima/guides/gradle-build.adoc new file mode 100644 index 00000000000..5bf56b2cc3d --- /dev/null +++ b/docs/nima/guides/gradle-build.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Gradle Guide +:description: Helidon Gradle Guide +:keywords: helidon, guide, gradle, build +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes Helidon's support for Gradle projects. + +include::{rootdir}/includes/guides/gradle-build.adoc[] diff --git a/docs/nima/guides/health.adoc b/docs/nima/guides/health.adoc new file mode 100644 index 00000000000..d9dcbe371ac --- /dev/null +++ b/docs/nima/guides/health.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima Health Check Guide +:description: Helidon health checks +:keywords: helidon, health check, health check, health, check +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to create a sample Helidon Nima project +that can be used to run some basic examples using both built-in and custom health checks. + diff --git a/docs/nima/guides/jlink-image.adoc b/docs/nima/guides/jlink-image.adoc new file mode 100644 index 00000000000..92102ae03bb --- /dev/null +++ b/docs/nima/guides/jlink-image.adoc @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Custom Runtime Images with `jlink` +:description: Helidon Custom Runtime Images +:keywords: helidon, guide, jlink, image +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to build a custom runtime image for your Helidon application +using Helidon's support for the JDK's `jlink` tool. + +include::{rootdir}/includes/guides/jlink-image.adoc[] diff --git a/docs/nima/guides/maven-build.adoc b/docs/nima/guides/maven-build.adoc new file mode 100644 index 00000000000..f8991012dfa --- /dev/null +++ b/docs/nima/guides/maven-build.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Maven Guide +:description: Helidon Maven Guide +:keywords: helidon, guide, maven, build +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes Helidon's support for Maven projects. + +include::{rootdir}/includes/guides/maven-build.adoc[] diff --git a/docs/nima/guides/metrics.adoc b/docs/nima/guides/metrics.adoc new file mode 100644 index 00000000000..5da5c534ae9 --- /dev/null +++ b/docs/nima/guides/metrics.adoc @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima Metrics Guide +:description: Helidon metrics +:keywords: helidon, metrics, microprofile, guide +:rootdir: {docdir}/../.. +include::{rootdir}/includes/se.adoc[] +:intro-project-name: {h1-prefix} + +include::{rootdir}/includes/nima.adoc[] + +== To do for Nima + diff --git a/docs/nima/guides/overview.adoc b/docs/nima/guides/overview.adoc index 8016a99616c..2b064988891 100644 --- a/docs/nima/guides/overview.adoc +++ b/docs/nima/guides/overview.adoc @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////// -= Helidon Níma Guides -:description: Helidon Níma Guides += Helidon Nima Guides +:description: Helidon Nima Guides :keywords: helidon, java, microservices, microprofile, guides :rootdir: {docdir}/../.. diff --git a/docs/nima/guides/performance-tuning.adoc b/docs/nima/guides/performance-tuning.adoc new file mode 100644 index 00000000000..9f0ac7f0536 --- /dev/null +++ b/docs/nima/guides/performance-tuning.adoc @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Performance Tuning +:description: Helidon Nima Performance Tuning +:feature-name: Performance Tuning +:microprofile-bundle: false +:keywords: helidon, nima, performance, tuning +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +In this guide you will find basic advice for performance tuning of your Helidon application. + + diff --git a/docs/nima/guides/quickstart.adoc b/docs/nima/guides/quickstart.adoc new file mode 100644 index 00000000000..aef25ff8b52 --- /dev/null +++ b/docs/nima/guides/quickstart.adoc @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima Quickstart +:description: Helidon Nima Quickstart Guide +:keywords: helidon +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +TO DO FOR NIMA - OLD SE VERSION for reference. This guide describes a basic example of an Helidon Nima application using Docker and Kubernetes. + +== What You Need + +For this 5 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + +== Generate The Project + +Generate the project sources using one (or both) of the Helidon Maven + archetypes. The result is a simple project that shows the basics of configuring + the WebServer and implementing basic routing rules. + +[source,bash,subs="attributes+"] +.Run the Maven archetype +---- +mvn -U archetype:generate -DinteractiveMode=false \ + -DarchetypeGroupId=io.helidon.archetypes \ + -DarchetypeArtifactId=helidon-quickstart-se \ + -DarchetypeVersion={helidon-version} \ + -DgroupId=io.helidon.examples \ + -DartifactId=helidon-quickstart-se \ + -Dpackage=io.helidon.examples.quickstart.se +---- + +The archetype generates a Maven project in your current directory +(for example, `helidon-quickstart-se`). Change into this directory. + +[source,bash] +---- +cd helidon-quickstart-se +---- + +TIP: If you want to use the generated project as a starter +for your own application, then you can replace groupId, artifactId +and package with values appropriate for your application. + +[source,bash] +.Build the Application +---- +mvn package +---- + +The project builds an application jar for the example and saves all runtime +dependencies in the `target/libs` directory. This means you can easily start the + application by running the application jar file: + +[source,bash] +.Run the application +---- +java -jar target/helidon-quickstart-se.jar +---- + +The example is a very simple "Hello World" greeting service. It supports GET + requests for generating a greeting message, and a PUT request for changing the + greeting itself. The response is encoded using JSON. +For example: + +[source,bash] +.Try the Application +---- +curl -X GET http://localhost:8080/greet +{"message":"Hello World!"} + +curl -X GET http://localhost:8080/greet/Joe +{"message":"Hello Joe!"} + +curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting + +curl -X GET http://localhost:8080/greet/Jose +{"message":"Hola Jose!"} +---- + +== Health and Metrics + +Helidon provides built-in support for health and metrics endpoints. + +[source,bash] +.Health +---- +curl -s -X GET http://localhost:8080/health +---- + +[source,bash] +.Metrics in Prometheus Format +---- +curl -s -X GET http://localhost:8080/metrics +---- + +[source,bash] +.Metrics in JSON Format +---- +curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics +---- + +== Build a Docker Image + +The project also contains a Dockerfile so that you can easily build and run a + Docker image. To build the Docker image, you need to have Docker installed and + running on your system. + +[source,bash] +.Docker build +---- +docker build -t helidon-quickstart-se . +---- + +[source,bash] +.Run Docker Image +---- +docker run --rm -p 8080:8080 helidon-quickstart-se:latest +---- + +Then you can try the application as you did before. + +== Deploy the application to Kubernetes + +If you don't have access to a Kubernetes cluster, you can +xref:../../about/kubernetes.adoc[install one on your desktop]. +Then deploy the example: + +[source,bash] +.Verify connectivity to cluster +---- +kubectl cluster-info +kubectl get nodes +---- + +[source,bash] +.Deploy the application to Kubernetes +---- +kubectl create -f app.yaml +kubectl get pods # Wait for quickstart pod to be RUNNING +---- + +The step above created a service that is exposed into any node port. Lookup + the service to find the port. + +[source,bash] +.Lookup the service +---- +kubectl get service helidon-quickstart-se +---- + +Note the PORTs. You can now exercise the application as you did before but use + the second port number (the NodePort) instead of 8080. For example: + +[source,bash] +curl -X GET http://localhost:31431/greet + +After you're done, cleanup. + +[source,bash] +.Remove the application from Kubernetes +---- +kubectl delete -f app.yaml +---- + +== Building Native and Custom Runtime Images + +Helidon also includes support for GraalVM Native Images and Java Custom +Runtime Images. For more information see: + +* xref:graalnative.adoc[GraalVM Native Images] +* xref:jlink-image.adoc[Custom Runtime Images using `jlink`] + +== The Helidon CLI + +With the Helidon CLI you can create additional types of Helidon applications and +use the "dev loop" to do fast, iterative development. +xref:../../about/cli.adoc[Try it now]. diff --git a/docs/nima/guides/security-oidc.adoc b/docs/nima/guides/security-oidc.adoc new file mode 100644 index 00000000000..82e32565cd7 --- /dev/null +++ b/docs/nima/guides/security-oidc.adoc @@ -0,0 +1,621 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima OIDC Security Provider Guide +:description: Helidon OIDC Security Provider +:keywords: helidon, security, guide, oidc, provider +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +REVIEW FOR NIMA --This guide describes how to setup Keycloak and Helidon +to secure your application with OIDC security provider. + +== What You Need + +For this 20 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + +In addition, you will need to install and configure the following: + +* <> +* <> +* <> +* <> +* <> +* <> + +== 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. +Its built on top of OAuth 2.0 and provides full-fledged authentication and authorization protocols. + +== Keycloak Installation [[Keycloak-Installation]] + +=== On Docker + +To install Keycloak with Docker, open a terminal and make sure the port 8080 is free. + +[source,bash] +.Enter the following command +---- +docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:11.0.2 +---- + +This will start Keycloak on local port 8080. It will create the admin user with username `admin` and password `admin` +Feel free to modify 11.0.2 by any keycloak version of your wish. +If you are running docker behind a proxy server, make sure it is either configured into docker or +disabled. Otherwise, you might face a connection timeout because docker cannot download the required data. + +To verify that Keycloak is running correctly, go to the admin console : http://localhost:8080/auth/admin +Log in using the username and password mentioned above: `admin`. + +You should be logged in successfully, and it prompts the admin console. + +=== On JDK + +Download the last version of Keycloak from Keycloak website : https://www.keycloak.org/downloads +In the table Server choose Standalone server distribution. ZIP or Tar format are available, click on either +to download Keycloak. + +After extracting the archive file, you should have a directory named keycloak followed by the version. For example, +if you chose version 11.0.2, the folder must be named keycloak-11.0.2. + +Open keycloak folder to make it your current directory. +[source,bash] +.Run this command from command prompt to open the directory: +---- +cd keycloak-11.0.2 +---- + +== Start Keycloak + +To start keycloak and have it ready for further steps, run the following command. + +[source,bash] +.On Linux run: +---- +bin/standalone.sh +---- + +[source,bash] +.On Windows run: +---- +bin/standalone.bat +---- + +Keycloak runs on localhost:8080 by default. + +=== Create an Admin User + +You need to create an admin user because it does not come by default when installing Keycloak. +To do this, open http://localhost:8080/auth in your favorite browser. + +A window `Welcome to Keycloak` should be prompted. If not, check if any error appear in the terminal. + +Fill the form by adding Username and Password. Click on `Create` to create the admin user. + +Above Administration Console should be printed "User created" in a green rectangle. + +To check that the admin user was created correctly, click on Administration user which should redirect you +to a Login form. Enter the Username and Password created earlier to log in. + +After successfully logged in, the admin console is prompted. + +== Setup Keycloak [[Setup-Keycloak]] + +To setup Keycloak properly, go to the admin console: http://localhost:8080/auth/admin + +If you are using Docker, use Username `admin` and password `admin` as it is the default admin user. +Otherwise, use the username and password you used to create the admin user. + +=== Create a Realm + +A realm is the place where groups of applications, and their environment, can be created. It gathers : + +- One or several applications +- One or several users +- Sessions +- Events +- Clients and their scopes + +By default, there is a realm called `Master`. It is used to manage Keycloak. It is not recommended to associate your +application with this realm as it could disturb Keycloak functioning. + +To create a new realm to manage your application: + +. Open Keycloak admin console http://localhost:8080/auth/admin. +. Hover the mouse over the dropdown in the top-left corner where it says `Master`, and press `Add realm`. +. Fill the form by adding the realm name, `myRealm` for example. +. Click on `Create` to create the new realm. + +To verify that your realm is created, on the top-left corner where it said `Master` previously +should be now your realm name or `myRealm` is you followed the example. + +To switch from a realm to another, hover the realm name, and the other realm created appear in the dropdown. +Click on any realm name to change the current realm. Make sure all configuration or modification are saved before changing +the current realm or be subject to lose your configuration. + +=== Create a User + +Initially there are no users in a new realm. An unlimited number of user can be created per realm. +A realm contains resources such as client which can be accessed by users. + +To create a new user: + +. Open the Keycloak admin console: http://localhost:8080/auth/admin +. Click on `Users` in the left menu +. Press `Add user` +. Fill the form (Username is the only mandatory field) with this value Username: `myUser` +. Click `Save` + +A new user is just created but it needs a password to be able to login. To initialize it, do this: + +. Click on `Credentials` at the top of the page, under `Myuser`. +. Fill `Password` and `Password confirmation` with the user password of your choice. +. If the `Temporary` field is set to `ON`, the user has to update password on next login. Click `ON` +to make it `OFF` and prevent it. +. Press `Set Password`. +. A pop-up window is popping off. Click on `Set Password` to confirm the new password. + +To verify that the new user is created correctly: + +. Open the Keycloak account console: `http://localhost:8080/auth/realms/myRealm/account`. +. Login with `myUser` and password chosen earlier. + +You should now be logged-in to the account console where users can manage their accounts. + +=== Create a Client + +To create your first client: + +. Open the Keycloak admin console: http://localhost:8080/auth/admin. +. Make sure the current realm is `myRealm` and not `Master`. +. Navigate to the left menu, into configure section, click on `Clients`. This window displays a table with every client +from the realm. +. Click on `Create`. +. Fill the following: +.. `Client ID` : `myClientID` +.. `Client Protocol` : `openid-connect` +. Press `Save` +.. Modify `Access type` : `confidential` +.. Update `Valid Redirect URIs` : http://localhost:7987/* +.. Click on `+` to add the new URI. +. Click on `Save`. + +A new tab named `Credentials` is created. Click on it to access this new tab. + +- Select `Client Authenticator` : `Client ID and Secret` +- Click on `generate secret` to generate client secret. + +Keycloak is now configured and ready. Keep keycloak running on your terminal and open a new tab to +setup Helidon. + +== Setup Helidon [[Setup-Helidon]] + +Use the Helidon SE Maven archetype to create a simple project. It will be used as an example +to show how to setup Helidon. Replace `{helidon-version}` by the latest helidon version. +It will download the quickstart project into the current directory. + +[source,bash,subs="attributes+"] +.Run the Maven archetype +---- +mvn -U archetype:generate -DinteractiveMode=false \ + -DarchetypeGroupId=io.helidon.archetypes \ + -DarchetypeArtifactId=helidon-quickstart-se \ + -DarchetypeVersion={helidon-version} \ + -DgroupId=io.helidon.examples \ + -DartifactId=helidon-quickstart-se \ + -Dpackage=io.helidon.examples.quickstart.se +---- + +[source,bash] +.The project will be built and run from the helidon-quickstart-se directory: +---- +cd helidon-quickstart-se +---- + +=== Update Project Dependencies + +Update the pom.xml file and add the following Helidon dependency to the `` section. + +[source,xml] +.Add the following dependency to `pom.xml`: +---- + + io.helidon.security.providers + helidon-security-providers-oidc-reactive + +---- + +=== Add OIDC Security Properties + +The OIDC security provider configuration can be joined to helidon configuration file. +This file is located here: `src/main/resources/application.yaml`. It can be easily used to configure the web server +without modifying application code. + +[source,yaml] +.Add the following line to application.yaml +---- +security: + providers: + - abac: + # Adds ABAC Provider - it does not require any configuration + - oidc: + client-id: "myClientID" // <1> + client-secret: "Client secret generated into Keycloak client credential" // <2> + identity-uri: "http://localhost:8080/auth/realms/myRealm" // <3> + audience: "account" + header-use: "true" + # proxy-host should be defined if you operate behind a proxy, can be removed otherwise + proxy-host: "" + frontend-uri: "http://localhost:7987" // <4> + server-type: "oidc" + web-server: + # protected paths on the web server + paths: // <5> + - path: "/greet" + methods: ["get"] + authenticate: true +---- +<1> `client-id` must be the same as the one configure in keycloak. +<2> The client secret generate by Keycloak during `Create a client` section. +<3> `identity-uri` is used to redirect the user to keycloak. +<4> `frontend-uri` will direct you back to the application. +<5> `paths` section defines the protected application's path. + +Make sure keycloak and the application are not running on the same port. +The application port value can be changed into application.yaml. + +[source,yaml] +.Change these properties to configure the server host and port +---- +server: + port: 7987 + host: localhost +---- + +If the port 7987 is already used, check what port is free on your machine. + +[source,yaml] +.Replace the old port into application.yaml +---- +server: + port: "{Your-new-port}" + +frontend-uri: "http://localhost:{Your-new-port}" +---- + +=== Configure Web Server + +Once the properties are added, the web server must be setup. +The `Main.createRouting` method gather all configuration properties. + +[source,java] +.Add the following to `Main.createRouting` method +---- +import io.helidon.security.Security; +import io.helidon.security.integration.webserver.WebSecurity; +import io.helidon.security.providers.oidc.reactive.OidcSupport; + +Security security = Security.create(config.get("security")); // <1> + +return Routing.builder() + .register(WebSecurity.create(security, config.get("security"))) // <2> + .register(OidcSupport.create(config)) // <3> +---- +<1> Create the Helidon `Security` instance using configuration. +<2> Register Helidon `WebSecurity` instance using security instance and configuration. +<3> Register Helidon `OidcSupport` instance. + +That code is extracting security properties from application.yaml into two steps. +First the Security instance is used to bootstrap security, so the WebSecurity instance +can integrate security into Web Server. +Then, OidcSupport instance registers the endpoint to which OIDC redirects browser after a successful login. + +Helidon sample is now setup and ready. + +== Build the Application + +[source,bash] +.Build the application, skipping unit tests, then run it: +---- +mvn package -DskipTests=true +java -jar target/helidon-quickstart-se.jar +---- + +The tests must be skipped, otherwise it produces test failure. As the `/greet` endpoint for GET request is +now protected, its access is limited, and the tests are not built to take oidc security in account. + +. Open your favourite browser and try to access `http://localhost:7987/greet/Michael`. +. You should not be redirected and receive greeting from the application. +. Enter the following into URL : `http://localhost:7987/greet`. +. Keycloak redirect you to its login page. +. Enter the username and associated password: +.. `Username` : `myUser` +.. `Password`: `password` +. After successful log in, keycloak redirect you to the `http://localhost:7987/greet` endpoint and print Hello word. +. Press `Ctrl+C` to stop the application. + +From the actual settings, the user needs to log in only once, then Keycloak saves all the connection data. + +=== Test Keycloak Process with Postman [[Test-Keycloak-process-with-Postman]] + +Keycloak supports many authentication and authorization flows, but only two of them will be shown. This section +describes another way you can get an access token or refresh a token or identity token. The identity token contains +information about the user. The access token contains access information that the application can use to determine what +resources the user is allowed to access. Once expired, the refresh token allows the application to obtain a new access +token. As these tokens contain sensitive information, they are valid for a very short period. It is possible to make them +last longer in order to let you manipulate them with Postman. To do so: + +1. Open the Postman Console. +2. Click on the `Realm Setting` in the left menu. +3. Navigate to the `Tokens` tab. + +You can increase the access token lifespan. + +==== Authorization Code Flow + +The Authorization Code flow is suitable for browser-based applications. It is composed of three main steps: + +1. The browser visits the application. The user is not logged in, so it redirects the browser to Keycloak which requires +username and password for authentication. +2. Keycloak authenticates the user and returns a temporary authorization code as a query parameter in the URL. +3. The authorization code is used to get access and refresh token from Keycloak token endpoint. + +For the first step, paste the following URL into your browser: +`http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/auth?client_id=myClientID&response_type=code`. +The first part of the url `http:/../auth` is the Keycloak endpoint to request an authorization code. Two query +parameters are provided, the client id and the response type. +Press enter and Keycloak responds with different URL containing a query parameter `code`. You successfully received +the authorization code. + +In order to achieve the third step, we can use Postman to exchange the authorization code for tokens. In Postman, +select the Http POST method. Keycloak endpoint to get token is the following: +`http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/token`. +In the body of the request, select `x-www-form-urlencoded` type. Add the following data: + +[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"}] +---- + +Do not forget to replace the `client secret` by its value (generated during Create a Client), and `authorization code` +by the code value in the query parameter. Send the request by pressing `Send`. Keycloak returns an access token and +a refresh token. + +==== Resource Owner Password Credentials Grant (Direct Access Grants) + +The Direct Access Grants flow is used by REST clients that want to request tokens on behalf of a user. +To use Postman to make this request on behalf of `myuser`, select the GET method and enter this URL: +`http://localhost:7987/greet/`. Under `Authorization` tab, select authorization type`OAuth 2.0`. Under it, complete the +sentence `Add authorization data to` with `Request Headers`, and complete the required fields. + +[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"}] +---- + +Again, make sure to replace `client secret` by the actual client secret. Click on `Get New Access Token`. A popup +window appears with Authentication complete, click on proceed to display access, refresh and identity token. +Copy and paste the access token to `Access Token` field and press `Send`. Helidon greeting application sends back +`Hello World !`. + +==== Update Tests to the Secure Environment + +At this stage of the application, tests cannot pass because of OIDC security. The only way to authenticate a user is +through the front end of that server which can be accessed with the browser for example. + +In order to keep security and test the application locally, a new security provider must be setup. By adding specific +configuration to the tests, it is possible to override the application configuration. + +The following explains how to set a basic authentication instead of oidc security provider only for the tests. Which means, +at the end of this guide, the application will be secured by oidc security provider, and the tests will use basic authentication. + +[source,xml] +.Add the following dependency to `pom.xml`: +---- + + io.helidon.security.providers + helidon-security-providers-http-auth + test + +---- + +In the test folder `helidon-quickstart-se/src/test`: + +[source,bash] +.Create a new directory and another one inside +---- +mkdir resources +cd resources +touch application.yaml +---- + +Open the application.yaml file + +[source,yaml] +.Copy these properties into application.yaml +---- +app: + greeting: "Hello" + +server: + port: 7987 + host: localhost + +security: + providers: + - abac: + # Adds ABAC Provider - it does not require any configuration + - http-basic-auth: + users: + - login: "jack" + password: "jackIsGreat" + - oidc: + client-id: "Your client ID" // <1> + client-secret: "Your client secret" // <2> + identity-uri: "http://localhost:8080/auth/realms/myRealm" + audience: "account" + frontend-uri: "http://localhost:7987" + server-type: "oidc" + web-server: + # protected paths on the web server - do not include paths served by Jersey, as those are protected directly + paths: + - path: "/greet" + methods: ["get"] + authenticate: true +---- +<1> Replace this field by your Keycloak client ID. +<2> Replace this field by your Keycloak client Password. + +Add the `http-basic-auth` properties in the security -> providers property section. This configuration will be used +by the tests instead of the `java/resources/application.yaml`. + +In the `MainTest.java` file, tests need to be modified to check the application security when accessing `/greet` path with a +`GET` method. + +[source,java] +.Import the following class: +---- +import java.util.Base64; +import io.helidon.common.http.Http; +---- + +[source,java] +.Replace the first webclient call by this one into testHelloWorld method: +---- +webClient.get() + .path("/greet") + .request() + .thenAccept(response -> Assertions.assertEquals(401,response.status().code())) + .toCompletableFuture() + .get(); +---- + +This piece of code uses the webclient to access the application on `/greet` path with a `GET` method. The http basic +authentication security protects this path, so the client should receive an HTTP 401 code for unauthorized. + +Only `jack` user has access to this part of the application. + +[source,java] +.Add new check to the testHelloWorld method: +---- +webClient.get() + .path("/greet") + .headers(headers -> { + String encoding = Base64.getEncoder().encodeToString("jack:jackIsGreat".getBytes()); + headers.add(Http.Header.AUTHORIZATION, "Basic " + encoding); + return headers; + }) + .request(JsonObject.class) + .thenAccept(jsonObject -> Assertions.assertEquals("Hello World!", jsonObject.getString("message"))) + .toCompletableFuture() + .get(); +---- + +The username and password are encoded and placed inside the header in order to authenticate as jack to access the application. +If the authentication is successful, the application send the `Hello World` back as a `JsonObject`. + +Now, the project can be build without skipping test. + +[source,bash] +.Build the project +---- +mvn clean install +---- + +==== Restrict Access to a Specific Role [[Restrict-access-to-a-specific-role]] + +To give less access to an endpoint, it is possible to configure user role. So the application will only grant access +to the user with the required role. + +Add a user and roles to the `helidon-quickstart-se/src/test/resources/application.yaml`. + +[source,yaml] +.Add jack role and create a new user named john: +---- +- http-basic-auth: + users: + - login: "jack" + password: "jackIsGreat" + roles: [ "admin", "user" ] + - login: "john" + password: "johnPassword" + roles: [ "user" ] +---- + +Into the `web-server` section, the `roles-allowed` parameter defines which roles have access +to the protected path and method. + +[source,yaml] +.Add `admin` role +---- +web-server: + # protected paths on the web server - do not include paths served by Jersey, as those are protected directly + paths: + - path: "/greet" + methods: ["get"] + roles-allowed: "admin" + authenticate: true +---- + +Now, only Jack has access to secure endpoint as he has an admin role. Jhon, as a simple user, can not access it. +Once it is done, go to the tests to check the application behavior. +The test from previous section is still passing as jack has access. + +The user `john` has only the `user` role so when accessing protected endpoint, a 403 (Forbidden) http code is returned. + +[source,java] +.Check that john does not have access +---- +webClient.get() + .path("/greet") + .headers(headers -> { + String encoding = Base64.getEncoder().encodeToString("john:johnPassword".getBytes()); + headers.add(Http.Header.AUTHORIZATION,"Basic " + encoding); + return headers; + }) + .request() + .thenAccept(response -> Assertions.assertEquals(403, response.status().code())) + .toCompletableFuture() + .get(); +---- + +[source,bash] +.Build the project +---- +mvn clean install +---- + +The tests pass, and your application is secured with specific roles in addition to user IDs. \ No newline at end of file diff --git a/docs/nima/guides/tracing.adoc b/docs/nima/guides/tracing.adoc new file mode 100644 index 00000000000..762ee78299c --- /dev/null +++ b/docs/nima/guides/tracing.adoc @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima Tracing Guide +:description: Helidon tracing +:keywords: helidon, tracing, microprofile, guide +:toc: +:rootdir: {docdir}/../.. +:imagesdir: {rootdir}/images + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to create a sample Helidon Nima project +that can be used to run some basic examples using tracing with a Helidon Nima application. + +== What You Need + +For this 30 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + diff --git a/docs/nima/guides/webclient.adoc b/docs/nima/guides/webclient.adoc new file mode 100644 index 00000000000..dc78ecc04de --- /dev/null +++ b/docs/nima/guides/webclient.adoc @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Nima WebClient Guide +:description: Helidon WebClient +:keywords: helidon, WebClient, web, client +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how to create a sample Helidon Nima project +that can be used to run some basic examples using WebClient. + +== What you need + +For this 15 minute tutorial, you will need the following: + +include::{rootdir}/includes/prerequisites.adoc[tag=prerequisites] + + diff --git a/docs/nima/health.adoc b/docs/nima/health.adoc new file mode 100644 index 00000000000..e470fe226b9 --- /dev/null +++ b/docs/nima/health.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += 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/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/integrations/hcv.adoc b/docs/nima/integrations/hcv.adoc new file mode 100644 index 00000000000..3c0f630589b --- /dev/null +++ b/docs/nima/integrations/hcv.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += HashiCorp Vault +:description: Helidon HashiCorp Vault integration +:keywords: vault, hashicorp +:feature-name: HashiCorp Vault +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/integrations/neo4j.adoc b/docs/nima/integrations/neo4j.adoc new file mode 100644 index 00000000000..0156fb3301e --- /dev/null +++ b/docs/nima/integrations/neo4j.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Neo4j +:description: Neo4j integration +:keywords: neo4j +:feature-name: Neo4j +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/integrations/oci.adoc b/docs/nima/integrations/oci.adoc new file mode 100644 index 00000000000..cac5184f9a9 --- /dev/null +++ b/docs/nima/integrations/oci.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Oracle Cloud Infrastructure Integration +:description: Helidon OCI Integration +:keywords: oci +:feature-name: OCI Integration +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/introduction.adoc b/docs/nima/introduction.adoc index dce52141e0b..a43a6bead9c 100644 --- a/docs/nima/introduction.adoc +++ b/docs/nima/introduction.adoc @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////// -= Helidon Níma += Helidon Nima :description: about Helidon Níma :keywords: helidon, java, microservices, reactive, virtual threads :rootdir: {docdir}/.. diff --git a/docs/nima/metrics/metrics-capable-components.adoc b/docs/nima/metrics/metrics-capable-components.adoc new file mode 100644 index 00000000000..caf26bfaab4 --- /dev/null +++ b/docs/nima/metrics/metrics-capable-components.adoc @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Metrics-Capable Modules +:description: Helidon Nima metrics-capable components +:keywords: helidon, metrics, metrics-capable, microprofile, guide +:intro-project-name: {h1-prefix} +:chk: icon:check[] +:x: icon:times[] +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/metrics/metrics.adoc b/docs/nima/metrics/metrics.adoc new file mode 100644 index 00000000000..661a3f6f853 --- /dev/null +++ b/docs/nima/metrics/metrics.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Metrics in Helidon Nima +:description: Helidon metrics +:keywords: helidon, metrics +:feature-name: metrics +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/metrics/micrometer.adoc b/docs/nima/metrics/micrometer.adoc new file mode 100644 index 00000000000..6382d5636b3 --- /dev/null +++ b/docs/nima/metrics/micrometer.adoc @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Micrometer Metrics +:description: Helidon Micrometer integration +:keywords: micrometer, helidon, metrics, integration +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/metrics/prometheus-exemplar-support.adoc b/docs/nima/metrics/prometheus-exemplar-support.adoc new file mode 100644 index 00000000000..6c6992e188c --- /dev/null +++ b/docs/nima/metrics/prometheus-exemplar-support.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Metrics Support for Exemplars +:description: Helidon metrics +:keywords: helidon, metrics, exemplar, prometheus, OpenMetrics +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] +include::{rootdir}/includes/metrics/prometheus-exemplar-support.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/openapi/openapi-generator.adoc b/docs/nima/openapi/openapi-generator.adoc new file mode 100644 index 00000000000..ba47963d875 --- /dev/null +++ b/docs/nima/openapi/openapi-generator.adoc @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += OpenAPI-based Code Generation diff --git a/docs/nima/openapi/openapi-ui.adoc b/docs/nima/openapi/openapi-ui.adoc new file mode 100644 index 00000000000..da96606ee8b --- /dev/null +++ b/docs/nima/openapi/openapi-ui.adoc @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += OpenAPI UI diff --git a/docs/nima/openapi/openapi.adoc b/docs/nima/openapi/openapi.adoc new file mode 100644 index 00000000000..6745c1f73d2 --- /dev/null +++ b/docs/nima/openapi/openapi.adoc @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += OpenAPI in Helidon +:description: Helidon Nima OpenAPI Support +:keywords: helidon, nima, openapi +:feature-name: OpenAPI +:rootdir: {docdir}/../.. + + + + + +== TO DO FOR NIMA + diff --git a/docs/nima/pico/pico_intro.adoc b/docs/nima/pico/pico_intro.adoc new file mode 100644 index 00000000000..31f21362e16 --- /dev/null +++ b/docs/nima/pico/pico_intro.adoc @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Pico +:description: about Helidon Pico +:keywords: helidon, java, microservices, reactive, virtual threads + + + + + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon Pico //explain what it is and what it can do. + +== Maven Coordinates + +To enable Pico add the following dependencies to your project's `pom.xml` (see Managing Dependencies). + +.Pico dependencies +[source,java] + +---- + + io.helidon.pico + helidon-pico-api + ${helidon.version} + + + io.helidon.pico + helidon-pico-types + ${helidon.version} + +---- + +== Usage + +Once you have planned how each of your resources should support Pico, you specify the Pico behavior in one of two ways: + + + +== API + +Adding Pico behavior to your Helidon Níma application involves just a few simple steps. + +=== Maven Coordinates + +.Pico API pom.xml +[source, java] +---- + helidon-pico-api + Helidon Pico API +---- + + +=== Types + +The Pico API provides types that are generally useful at compile time to assign special meaning to the type. In this way it also helps with readability and intentions of the code itself. + +.Pico types pom.xml +[source, java] +---- + helidon-pico-types + Helidon Pico Types +---- + + + * {@link io.helidon.pico.api.Contract} - signifies that the type can be used for lookup in the service registry. + * {@link io.helidon.pico.api.ExternalContracts} - same as Contract, but applied to the implementation class instead. + * {@link io.helidon.pico.api.RunLevel} - ascribes meaning for when the service should start. + + + + + + +== Configuration + +Pico Config builder + + + +== Examples + +//optional + +== Additional Information + + + + diff --git a/docs/nima/reactive-messaging.adoc b/docs/nima/reactive-messaging.adoc new file mode 100644 index 00000000000..420fff42f51 --- /dev/null +++ b/docs/nima/reactive-messaging.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Reactive Messaging +:h1Prefix: Nima +:toc: +:toc-placement: preamble +:description: Reactive Messaging support in Helidon Nima +:keywords: helidon, se, messaging +:feature-name: Reactive Messaging +:rootdir: {docdir}/.. + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/reactivestreams/engine.adoc b/docs/nima/reactivestreams/engine.adoc new file mode 100644 index 00000000000..35e5ccf95bd --- /dev/null +++ b/docs/nima/reactivestreams/engine.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Helidon Reactive Engine +:description: Dependency-less reactive operators +:keywords: helidon, reactive, streams, multi, single +:feature-name: Reactive Engine +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +include::{rootdir}/includes/reactivestreams/engine.adoc[lines=21..] diff --git a/docs/nima/reactivestreams/rsoperators.adoc b/docs/nima/reactivestreams/rsoperators.adoc new file mode 100644 index 00000000000..508e7df51f9 --- /dev/null +++ b/docs/nima/reactivestreams/rsoperators.adoc @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += MicroProfile Reactive Streams Operators +:description: MicroProfile Reactive Streams Operators support in Helidon Nima +:keywords: helidon, nima, microprofile, reactivestreams +:feature-name: Reactive Streams +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] +include::{rootdir}/includes/reactivestreams/rsoperators.adoc[] diff --git a/docs/nima/scheduling.adoc b/docs/nima/scheduling.adoc new file mode 100644 index 00000000000..72c0973db33 --- /dev/null +++ b/docs/nima/scheduling.adoc @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Overview +:toc: +:toc-placement: preamble +:description: Scheduling in Helidon Nima +:keywords: helidon, nima, scheduling +:h1Prefix: Nima +:feature-name: Scheduling +:rootdir: {docdir}/.. + + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/containers-integration.adoc b/docs/nima/security/containers-integration.adoc new file mode 100644 index 00000000000..bb699e09d18 --- /dev/null +++ b/docs/nima/security/containers-integration.adoc @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Containers Integration +:description: Helidon Security containers integration +:keywords: helidon, security +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/extensibility.adoc b/docs/nima/security/extensibility.adoc new file mode 100644 index 00000000000..4e34e736654 --- /dev/null +++ b/docs/nima/security/extensibility.adoc @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Extending Security +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +This guide describes how you can extend the Security component. + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/introduction.adoc b/docs/nima/security/introduction.adoc new file mode 100644 index 00000000000..45f9057aae2 --- /dev/null +++ b/docs/nima/security/introduction.adoc @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Security Introduction +:description: Helidon Security introduction +:keywords: helidon, security +:feature-name: Security +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +Helidon Security provides authentication, authorization and auditing for your Helidon application. + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.security + helidon-security + +---- + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/jep-290.adoc b/docs/nima/security/jep-290.adoc new file mode 100644 index 00000000000..6f430424185 --- /dev/null +++ b/docs/nima/security/jep-290.adoc @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += JEP-290 + +:description: Helidon Security Java Serialization (JEP-290) +:keywords: helidon, security, serialization, java, JEP-290 +:metainf: META-INF/helidon/serial-config.properties +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +include::{rootdir}/includes/security/jep-290.adoc[lines=26..] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/providers.adoc b/docs/nima/security/providers.adoc new file mode 100644 index 00000000000..b87dab9c676 --- /dev/null +++ b/docs/nima/security/providers.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Security Providers +:description: Helidon Security providers +:keywords: helidon, security +:feature-name: Security Providers +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/security/tools.adoc b/docs/nima/security/tools.adoc new file mode 100644 index 00000000000..d27aee19215 --- /dev/null +++ b/docs/nima/security/tools.adoc @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Security Tools +:description: Helidon Security Tools +:keywords: helidon, security +:rootdir: {docdir}/../.. + +== TO DO FOR NIMA + diff --git a/docs/nima/tracing.adoc b/docs/nima/tracing.adoc new file mode 100644 index 00000000000..98febb62510 --- /dev/null +++ b/docs/nima/tracing.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Tracing +:description: Helidon Nima Tracing Support +:feature-name: Helidon Tracing +:keywords: helidon, tracing +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/webclient.adoc b/docs/nima/webclient.adoc new file mode 100644 index 00000000000..2ebfd3c79dd --- /dev/null +++ b/docs/nima/webclient.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += WebClient Introduction +:description: Helidon WebClient +:keywords: helidon, nima, rest, httpclient, webclient, reactive +:feature-name: WebClient +:rootdir: {docdir}/.. + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/webserver.adoc b/docs/nima/webserver.adoc new file mode 100644 index 00000000000..c54fddf0e21 --- /dev/null +++ b/docs/nima/webserver.adoc @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += WebServer Introduction +:description: Helidon Reactive WebServer Introduction +:keywords: helidon, reactive, reactive streams, reactive java, reactive webserver, tls +:feature-name: WebServer +:rootdir: {docdir}/.. +:requested-uri-discovery-inc: {rootdir}/includes/server/requested-uri-discovery.adoc + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/nima/websocket.adoc b/docs/nima/websocket.adoc new file mode 100644 index 00000000000..79154fb963a --- /dev/null +++ b/docs/nima/websocket.adoc @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2023 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += WebSocket Introduction +:description: Helidon WebSocket Introduction +:keywords: helidon, webserver, websocket, nima +:rootdir: {docdir}/.. + + +include::{rootdir}/includes/nima.adoc[] + +== TO DO FOR NIMA \ No newline at end of file diff --git a/docs/se/openapi.adoc b/docs/se/openapi.adoc index 11d493551e8..804a06e8637 100644 --- a/docs/se/openapi.adoc +++ b/docs/se/openapi.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2019, 2022 Oracle and/or its affiliates. + Copyright (c) 2019, 2023 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index b1c7beeb770..71ea1b83bd0 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -53,29 +53,17 @@ backend: value: "images/helidon_logo_dark.svg" items: - type: "PAGE" - title: "Documentation Overview" + title: "Helidon Documentation" source: "about/doc_overview.adoc" glyph: type: "icon" value: "lightbulb" - type: "PAGE" - title: "Get Started" - source: "about/prerequisites.adoc" - glyph: - type: "icon" - value: "rocket_launch" - - type: "PAGE" - title: "Helidon CLI" - source: "about/cli.adoc" - glyph: - type: "icon" - value: "terminal" - - type: "PAGE" - title: "Helidon Application Bootstraping" - source: "about/archetype.adoc" + title: "Documentation Overview" + source: "about/doc_sitemap.adoc" glyph: type: "icon" - value: "widgets" + value: "library_books" - type: "GROUPS" items: - type: "GROUP" @@ -88,12 +76,6 @@ backend: glyph: type: "icon" value: "lightbulb" - - type: "PAGE" - title: "Server-Sent Events" - source: "sse/sse.adoc" - glyph: - type: "icon" - value: "reply-all" - type: "MENU" title: "Guides" dir: "guides" @@ -102,39 +84,27 @@ backend: value: "explore" sources: - "overview.adoc" - - type: "GROUP" - title: "Helidon SE" - dir: "se" - items: - - type: "PAGE" - title: "Introduction" - source: "introduction.adoc" - glyph: - type: "icon" - value: "lightbulb" - - type: "MENU" - title: "Guides" - dir: "guides" - glyph: - type: "icon" - value: "explore" - sources: - - "overview.adoc" - - "quickstart.adoc" - "config.adoc" + - "dbclient.adoc" + - "graalnative.adoc" + - "gradle-build.adoc" - "health.adoc" + - "jlink-image.adoc" + - "maven-build.adoc" - "metrics.adoc" + - "performance-tuning.adoc" + - "quickstart.adoc" - "security-oidc.adoc" - "tracing.adoc" - - "migration.adoc" - - "migration_3x.adoc" - - "maven-build.adoc" - - "gradle-build.adoc" - - "graalnative.adoc" - - "jlink-image.adoc" - "webclient.adoc" - - "dbclient.adoc" - - "performance-tuning.adoc" + - type: "MENU" + title: "Pico" + dir: "pico" + glyph: + type: "icon" + value: "explore" + sources: + - "pico_intro.adoc" - type: "MENU" title: "Config" dir: "config" @@ -266,6 +236,12 @@ backend: - "tools.adoc" - "extensibility.adoc" - "jep-290.adoc" + - type: "PAGE" + title: "Server-Sent Events" + source: "sse/sse.adoc" + glyph: + type: "icon" + value: "reply_all" - type: "PAGE" title: "Tracing" source: "tracing.adoc" @@ -309,8 +285,6 @@ backend: - "security-oidc.adoc" - "tracing.adoc" - "mp-tutorial.adoc" - - "migration.adoc" - - "migration_3x.adoc" - "maven-build.adoc" - "gradle-build.adoc" - "graalnative.adoc" @@ -503,7 +477,189 @@ backend: source: "websocket.adoc" glyph: type: "icon" - value: "sync_alt" + value: "sync_alt" + - type: "GROUP" + title: "Helidon SE" + dir: "se" + items: + - type: "PAGE" + title: "Introduction" + source: "introduction.adoc" + glyph: + type: "icon" + value: "lightbulb" + - type: "MENU" + title: "Guides" + dir: "guides" + glyph: + type: "icon" + value: "explore" + sources: + - "overview.adoc" + - "quickstart.adoc" + - "config.adoc" + - "health.adoc" + - "metrics.adoc" + - "security-oidc.adoc" + - "tracing.adoc" + - "migration.adoc" + - "migration_3x.adoc" + - "maven-build.adoc" + - "gradle-build.adoc" + - "graalnative.adoc" + - "jlink-image.adoc" + - "webclient.adoc" + - "dbclient.adoc" + - "performance-tuning.adoc" + - type: "MENU" + title: "Config" + dir: "config" + glyph: + type: "icon" + value: "settings" + sources: + - "introduction.adoc" + - "hierarchical-features.adoc" + - "property-mapping.adoc" + - "supported-formats.adoc" + - "config-profiles.adoc" + - "mutability-support.adoc" + - "advanced-configuration.adoc" + - "extensions.adoc" + - type: "PAGE" + title: "CORS" + source: "cors.adoc" + glyph: + type: "icon" + value: "share" + - type: "PAGE" + title: "DB Client" + source: "dbclient.adoc" + glyph: + type: "icon" + value: "storage" + - type: "PAGE" + title: "Fault Tolerance" + source: "fault-tolerance.adoc" + glyph: + type: "icon" + value: "warning" + - type: "PAGE" + title: "GraalVM Native Image" + source: "aot.adoc" + glyph: + type: "icon" + value: "save" + - type: "PAGE" + title: "GraphQL Server" + source: "graphql.adoc" + glyph: + type: "icon" + value: "graphic_eq" + - type: "MENU" + title: "gRPC" + dir: "grpc" + glyph: + type: "icon" + value: "swap_horiz" + sources: + - "server.adoc" + - "client.adoc" + - type: "PAGE" + title: "Health Checks" + source: "health.adoc" + glyph: + type: "icon" + value: "favorite_outline" + - type: "MENU" + title: "Integrations" + dir: "integrations" + glyph: + type: "icon" + value: "filter_drama" + sources: + - "oci.adoc" + - "hcv.adoc" + - "neo4j.adoc" + - type: "MENU" + title: "Metrics" + dir: "metrics" + glyph: + type: "icon" + value: "av_timer" + sources: + - "metrics.adoc" + - "micrometer.adoc" + - "prometheus-exemplar-support.adoc" + - "metrics-capable-components.adoc" + - type: "MENU" + title: "OpenAPI" + dir: "openapi" + glyph: + type: "icon" + value: "donut_large" + sources: + - "openapi.adoc" + - "openapi-generator.adoc" + - "openapi-ui.adoc" + - type: "PAGE" + title: "Reactive Messaging" + source: "reactive-messaging.adoc" + glyph: + type: "icon" + value: "message" + - type: "MENU" + title: "Reactive Streams" + dir: "reactivestreams" + glyph: + type: "icon" + value: "waves" + sources: + - "engine.adoc" + - "rsoperators.adoc" + - type: "PAGE" + title: "Reactive Webserver" + source: "webserver.adoc" + glyph: + type: "icon" + value: "settings_ethernet" + - type: "PAGE" + title: "Scheduling" + source: "scheduling.adoc" + glyph: + type: "icon" + value: "access_alarm" + - type: "MENU" + title: "Security" + dir: "security" + glyph: + type: "icon" + value: "security" + sources: + - "introduction.adoc" + - "providers.adoc" + - "containers-integration.adoc" + - "tools.adoc" + - "extensibility.adoc" + - "jep-290.adoc" + - type: "PAGE" + title: "Tracing" + source: "tracing.adoc" + glyph: + type: "icon" + value: "timeline" + - type: "PAGE" + title: "Web Client" + source: "webclient.adoc" + glyph: + type: "icon" + value: "http" + - type: "PAGE" + title: "Websocket" + source: "websocket.adoc" + glyph: + type: "icon" + value: "timeline" - type: "HEADER" title: "Additional resources" - type: "LINK"