From 1f6667c4215faddebe5300f3d504a28afce583ec Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 19 Sep 2025 11:52:16 +0200 Subject: [PATCH 1/2] Initial documentation for Helidon Declarative --- docs/src/main/asciidoc/se/injection.adoc | 187 ++++++++++++++++++ .../docs/se/inject/DeclarativeExample.java | 121 ++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java diff --git a/docs/src/main/asciidoc/se/injection.adoc b/docs/src/main/asciidoc/se/injection.adoc index dd66d1c4236..f6cfa14c483 100644 --- a/docs/src/main/asciidoc/se/injection.adoc +++ b/docs/src/main/asciidoc/se/injection.adoc @@ -44,6 +44,7 @@ include::{rootdir}/includes/se.adoc[] - <> - <> - <> +- <> == Overview @@ -905,3 +906,189 @@ level2 created level2 destroyed level1 destroyed ---- + +== Declarative + +Helidon declarative programming model allows inversion of control style programming with all the performance benefits of Helidon SE. + +Our declarative approach has the following advantages: + +- Uses Helidon SE imperative code to implement features (i.e. performance is same as "pure" imperative application) +- Generates all the necessary code at build-time, to avoid reflection and bytecode manipulation at runtime + +Helidon declarative is currently a preview feature, though we welcome feedback! + +To create a declarative application, use the annotations provided in our Helidon SE modules (details below), and the maven plugin +described above to generate the binding. +In addition, the following section must be added to the `build` of the Maven `pom.xml` to enable annotation processors that +generate the necessary code: + +[source,xml] +---- + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.bundles + helidon-bundles-apt + ${helidon.version} + + + + + +---- + +The following features are currently implemented: + +- <> +- <> +- <> +- <> +- <> + + +[source,java] +.Example of a declarative main class +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_1, indent=0] +---- + +=== Configuration [[Dec-Config]] + +Configuration can be injected as a whole into any service, or a specific configuration option can be injected using `@Configuration.Value`. Default values can be defined using annotations in `@Default` + +Services available for injection: + +- `io.helidon.config.Config` + +Annotations: + +- `io.helidon.config.Configuration.Value` - define the configuration key to inject, on constructor parameter +- `io.helidon.common.Default.*` - define a default typed value, on the same constructor parameter + +Example of usage can be seen below in HTTP Server Endpoint example. + +=== HTTP Server Endpoint [[Dec-HTTP-Server]] + +To create an HTTP endpoint, simply annotate a class with `@RestServer.Endpoint`, and +add at least one method annotated with one of the HTTP method annotations, such as `@Http.GET`. + +Services available for injection: + +N/A + +Supported method parameters (no annotation required): + +- `io.helidon.webserver.http.ServerRequest` +- `io.helidon.webserver.http.ServerResponse` +- `io.helidon.common.context.Context` +- `io.helidon.common.security.SecurityContext` +- `io.helidon.security.SecurityContext` - in case `helidon-security` module is on the classpath + +Annotations on endpoint type: + +- `io.helidon.webserver.http.RestServer.Endpoint` - required annotation +- `io.helidon.webserver.http.RestServer.Listener` - to define the named listener this should be served on (named port/socket) +- `io.helidon.webserver.http.RestServer.Header` - header to return with each response from this endpoint +- `io.helidon.webserver.http.RestServer.ComputedHeader` - computed header to return with each response from this endpoint +- `io.helidon.http.Http.Path` - path (context) this endpoint will be available on + +Annotations on endpoint methods: + +- `io.helidon.webserver.http.RestServer.Header` - header to return with each response from this method +- `io.helidon.webserver.http.RestServer.ComputedHeader` - computed header to return with each response from this method +- `io.helidon.webserver.http.RestServer.Status` - status to return (if a custom one is required) +- `io.helidon.http.Http.Path` - path (context) this method will be available on (subpath of the endpoint path) +- `io.helidon.http.Http.GET` (and other methods) - definition of HTTP method this method will serve +- `io.helidon.http.Http.HttpMethod` - for custom HTTP method names (mutually exclusive with above) +- `io.helidon.http.Http.Produces` - what media type this method produces (return entity content type) +- `io.helidon.http.Http.Consumes` - what media type this method accepts (request entity content type) + +Annotations on method parameters: + +- `io.helidon.http.Http.Entity` - Request entity, a typed parameter is expected, will use HTTP media type modules to coerce into the correct type +- `io.helidon.http.Http.HeaderParam` - Typed HTTP request header value +- `io.helidon.http.Http.QueryParam` - Typed HTTP query value +- `io.helidon.http.Http.PathParam` - Typed parameter from path template + +[source,java] +.Example of an HTTP Server Endpoint +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_2, indent=0] +---- + +=== Typed HTTP Client [[Dec-HTTP-Client]] + +To create a typed HTTP client, create an interface annotated with `RestClient.Endpoint`, and at least one method annotated +with one fo the HTTP method annotations, such as `@Http.GET`. Methods can only have parameters annotated with one of the +`Http` qualifiers. + +Annotations on endpoint type: + +- `io.helidon.webclient.api.RestClient.Endpoint` - required annotation +- `io.helidon.http.Http.Path` - path (context) the server listens on +- `io.helidon.webclient.api.RestClient.Header` - header to include in every request to the server +- `io.helidon.webclient.api.RestClient.ComputedHeader` - header to compute and include in every request to the server + +Annotations on endpoint methods: + +- `io.helidon.webclient.api.RestClient.Header` - header to include in every request to the server +- `io.helidon.webclient.api.RestClient.ComputedHeader` - header to compute and include in every request to the server +- `io.helidon.http.Http.Path` - path (context) the server serves this endpoint method on +- `io.helidon.http.Http.GET` (and other methods) - definition of HTTP method this method will invoke +- `io.helidon.http.Http.HttpMethod` - for custom HTTP method names (mutually exclusive with above) +- `io.helidon.http.Http.Produces` - what media type this method produces (content type of entity from the server) +- `io.helidon.http.Http.Consumes` - what media type this method accepts (request entity content type) + +Annotations on method parameters: + +- `io.helidon.http.Http.Entity` - Request entity, a typed parameter is expected, will use HTTP media type modules to write to the request +- `io.helidon.http.Http.HeaderParam` - Typed HTTP header value to send +- `io.helidon.http.Http.QueryParam` - Typed HTTP query value to send +- `io.helidon.http.Http.PathParam` - Typed parameter from path template to construct the request URI + +[source,java] +.Example of a Typed HTTP Client +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_3, indent=0] +---- + + +=== Fault Tolerance [[Dec-FT]] + +Fault tolerance annotation allow adding features to methods on services. +The annotations can be added to any method that supports interception (i.e. methods that are not private). + +Method Annotations: + +- `io.helidon.faulttolerance.Ft.Retry` - allow retries +- `io.helidon.faulttolerance.Ft.Fallback` - fallback to another method that provides +- `io.helidon.faulttolerance.Ft.Async` - invoke method asynchronously +- `io.helidon.faulttolerance.Ft.Timeout` - invoke method with a timeout +- `io.helidon.faulttolerance.Ft.Bulkhead` - use bulkhead +- `io.helidon.faulttolerance.Ft.CircuitBreaker` - use circuit breaker + +[source,java] +.Example of Fault Tolerance Fallback +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_4, indent=0] +---- + +=== Scheduling [[Dec-Scheduling]] + +Scheduling allows service methods to be invoked periodically. + +Method annotations: + +- `io.helidon.scheduling.Scheduling.Cron` - execute with schedule defined by a CRON expression +- `io.helidon.scheduling.Scheduling.FixedRate` - execute with a fixed interval + +[source,java] +.Example of a fixed rate scheduled method +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_5, indent=0] +---- diff --git a/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java b/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java new file mode 100644 index 00000000000..b5167c9d164 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.docs.se.inject; + +import java.io.IOException; +import java.util.Map; + +import io.helidon.common.Default; +import io.helidon.common.media.type.MediaTypes; +import io.helidon.config.Configuration; +import io.helidon.faulttolerance.Ft; +import io.helidon.http.HeaderNames; +import io.helidon.http.Http; +import io.helidon.logging.common.LogConfig; +import io.helidon.scheduling.Scheduling; +import io.helidon.service.registry.Binding; +import io.helidon.service.registry.Service; +import io.helidon.service.registry.ServiceRegistryManager; +import io.helidon.webclient.api.RestClient; +import io.helidon.webserver.http.RestServer; + +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; + +@SuppressWarnings("deprecation") +public class DeclarativeExample { + private DeclarativeExample() { + } + + // tag::snippet_3[] + @RestClient.Endpoint("${greet-service.client.uri:http://localhost:8080}") + @RestClient.Header(name = HeaderNames.USER_AGENT_NAME, value = "my-client") + interface GreetClient { + @Http.GET + @Http.Produces(MediaTypes.APPLICATION_JSON_VALUE) + JsonObject getDefaultMessageHandler(); + } + // end::snippet_3[] + + // tag::snippet_1[] + @Service.GenerateBinding + public static class Main { + public static void main(String[] args) { + // configure logging + LogConfig.configureRuntime(); + + // start the "container" + ServiceRegistryManager.start(ApplicationBinding.create()); + } + } + // end::snippet_1[] + + // tag::snippet_2[] + @RestServer.Endpoint + @Http.Path("/greet") + @Service.Singleton + static class GreetEndpoint { + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Map.of()); + private final String greeting; + + GreetEndpoint(@Configuration.Value("app.greeting") @Default.Value("Hello") String greeting) { + this.greeting = greeting; + } + + @Http.GET + @Http.Produces(MediaTypes.APPLICATION_JSON_VALUE) + public JsonObject getDefaultMessageHandler() { + return JSON.createObjectBuilder() + .add("message", greeting + " World!") + .build(); + } + } + // end::snippet_2[] + + // tag::snippet_4[] + @Service.Singleton + static class AlgorithmService { + @Ft.Fallback(value = "fallbackAlgorithm", applyOn = IOException.class) + String algorithm() throws IOException { + // may throw an exception + return "some-algorithm"; + } + + String fallbackAlgorithm() { + return "default"; + } + } + // end::snippet_4[] + + // tag::snippet_5[] + @Service.Singleton + static class CacheService { + @Scheduling.FixedRate("PT5S") + void checkCache() { + // do something every 5 seconds + } + } + // end::snippet_5[] + + + private static class ApplicationBinding { + static Binding create() { + return null; + } + } +} From e8d6474c7a721c67bf2d60c947faa8d9f0838eb5 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 25 Sep 2025 15:34:52 +0200 Subject: [PATCH 2/2] Separate `injection` and `declarative` doc files. Updates to examples (added a few comments) Related additions (javadoc URIs, references fixes) --- .../main/asciidoc/includes/attributes.adoc | 4 + docs/src/main/asciidoc/se/discovery.adoc | 10 +- .../asciidoc/se/injection/declarative.adoc | 229 ++++++++++++++++++ .../se/{ => injection}/injection.adoc | 193 +-------------- docs/src/main/asciidoc/se/introduction.adoc | 2 +- docs/src/main/asciidoc/sitegen.yaml | 7 +- .../docs/se/inject/DeclarativeExample.java | 15 +- 7 files changed, 257 insertions(+), 203 deletions(-) create mode 100644 docs/src/main/asciidoc/se/injection/declarative.adoc rename docs/src/main/asciidoc/se/{ => injection}/injection.adoc (83%) diff --git a/docs/src/main/asciidoc/includes/attributes.adoc b/docs/src/main/asciidoc/includes/attributes.adoc index 24e1405e12a..c9058d2e46c 100644 --- a/docs/src/main/asciidoc/includes/attributes.adoc +++ b/docs/src/main/asciidoc/includes/attributes.adoc @@ -205,6 +205,7 @@ endif::[] :config-git-javadoc-base-url: {javadoc-base-url}/io.helidon.config.git :config-mapping-javadoc-base-url: {javadoc-base-url}/io.helidon.config.objectmapping :configurable-javadoc-base-url: {javadoc-base-url}/io.helidon.common.configurable +:context-javadoc-base-url: {javadoc-base-url}/io.helidon.common.context :cors-javadoc-base-url: {javadoc-base-url}/io.helidon.cors :discovery-javadoc-base-url: {javadoc-base-url}/io.helidon.discovery :faulttolerance-javadoc-base-url: {javadoc-base-url}/io.helidon.faulttolerance @@ -233,6 +234,7 @@ endif::[] :openapi-ui-javadoc-base-url: {javadoc-base-url}/io.helidon.integrations.openapi.ui :reactive-base-url: {javadoc-base-url}/io.helidon.common.reactive :scheduling-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.scheduling +:scheduling-se-javadoc-base-url: {javadoc-base-url}/io.helidon.scheduling :security-integration-jersey-base-url: {javadoc-base-url}/io.helidon.security.integration.jersey :security-integration-webserver-base-url: {javadoc-base-url}/io.helidon.webserver.security :service-registry-base-url: {javadoc-base-url}/io.helidon.service.registry @@ -243,10 +245,12 @@ endif::[] :types-javadoc-base-url: {javadoc-base-url}/io.helidon.common.types :webclient-javadoc-base-url: {javadoc-base-url}/io.helidon.webclient +:webclient-api-javadoc-base-url: {javadoc-base-url}/io.helidon.webclient.api :webserver-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver :webserver-staticcontent-javadoc-base-url: {webserver-javadoc-base-url}.staticcontent :webserver-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.cors :graphql-javadoc-base-url: {javadoc-base-url}/io.helidon.graphql.server +:security-javadoc-base-url: {javadoc-base-url}/io.helidon.security :security-provider-oidc-base-url: {javadoc-base-url}/io.helidon.security.providers.oidc :security-provider-httpauth-base-url: {javadoc-base-url}/io.helidon.security.providers.httpauth :security-provider-header-base-url: {javadoc-base-url}/io.helidon.security.providers.header diff --git a/docs/src/main/asciidoc/se/discovery.adoc b/docs/src/main/asciidoc/se/discovery.adoc index e6376ed493c..122bc6ad95f 100644 --- a/docs/src/main/asciidoc/se/discovery.adoc +++ b/docs/src/main/asciidoc/se/discovery.adoc @@ -82,11 +82,11 @@ value you supply will always be present as the last element in the set of resour === `Discovery` Acquisition -==== `Discovery` Acquisition Using xref:injection.adoc#_injection_points[Helidon Inject] +==== `Discovery` Acquisition Using xref:injection/injection.adoc#_injection_points[Helidon Inject] You can acquire a link:{discovery-javadoc-base-url}/io/helidon/discovery/Discovery.html[`io.helidon.discovery.Discovery` object] by -xref:injection.adoc#_injection_points[injecting] it into your Helidon SE application: +xref:injection/injection.adoc#_injection_points[injecting] it into your Helidon SE application: [source,java] .Acquiring a `Discovery` object using Helidon Inject @@ -97,16 +97,16 @@ include::{sourcedir}/se/DiscoverySnippets.java[tag=snippet_0,indent=0] ---- <1> Use the link:{service-registry-base-url}/io/helidon/service/registry/Service.Inject.html[`io.helidon.service.registry.Service.Inject` - annotation] to indicate that this constructor has an xref:injection.adoc#_injection_points[injection point]. + annotation] to indicate that this constructor has an xref:injection/injection.adoc#_injection_points[injection point]. <2> Here, the `discovery` constructor parameter is the injection point and will receive a non-`null` link:{discovery-javadoc-base-url}/io/helidon/discovery/Discovery.html[instance of `io.helidon.discovery.Discovery`]. <3> The constructor explicitly assigns the injected reference to the `discovery` instance field. -==== `Discovery` Acquisition Using the Helidon xref:injection.adoc#_programmatic_lookup[Service Registry] +==== `Discovery` Acquisition Using the Helidon xref:injection/injection.adoc#_programmatic_lookup[Service Registry] You can acquire a link:{discovery-javadoc-base-url}/io/helidon/discovery/Discovery.html[`io.helidon.discovery.Discovery` object] by -xref:injection.adoc#_programmatic_lookup[using the Helidon Service Registry] via the +xref:injection/injection.adoc#_programmatic_lookup[using the Helidon Service Registry] via the link:{service-registry-base-url}/io/helidon/service/registry/Services.html[`io.helidon.service.registry.Services` façade]: diff --git a/docs/src/main/asciidoc/se/injection/declarative.adoc b/docs/src/main/asciidoc/se/injection/declarative.adoc new file mode 100644 index 00000000000..0af3c075e1e --- /dev/null +++ b/docs/src/main/asciidoc/se/injection/declarative.adoc @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2025 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Declarative + +:description: Helidon SE Declarative +:keywords: helidon, se, injection, declarative +:h1Prefix: SE +:feature-name: Declarative +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/se.adoc[] + +== Contents + +- <> +- <> +- <> + +== Overview + +Helidon declarative programming model allows inversion of control style programming with all the performance benefits of Helidon SE. + +Our declarative approach has the following advantages: + +- Uses Helidon SE imperative code to implement features (i.e. performance is same as "pure" imperative application) +- Generates all the necessary code at build-time, to avoid reflection and bytecode manipulation at runtime +- It is based on xref:injection.adoc#Overview[Helidon Injection] +- Declarative features are in the same modules as Helidon SE features (i.e. does not require additional dependencies) + +[NOTE] +Helidon Declarative is an incubating feature. The APIs shown here are subject to change. These APIs will be finalized in a future release of Helidon. + +== Usage + +To create a declarative application, use the annotations provided in our Helidon SE modules (details under <>), and the maven plugin +described in xref:injection.adoc#generate-binding[Injection: Startup] to generate the binding. + +In addition, the following section must be added to the `build` of the Maven `pom.xml` to enable annotation processors that generate the necessary code: + +[source,xml] +---- + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.bundles + helidon-bundles-apt + ${helidon.version} + + + + + +---- + + +== Features + +The following features are currently implemented: + +- <> +- <> +- <> +- <> +- <> + +A Helidon Declarative application should be started using the generated application binding, to ensure no lookup and no reflection. +The call to `ServiceRegistryManager.start` ensures that all services with a defined `RunLevel` are started, including Helidon WebServer, Scheduled services etc. + +[source,java] +.Example of a declarative main class +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_1, indent=0] +---- + +=== Configuration [[Dec-Config]] + +Configuration can be injected as a whole into any service, or a specific configuration option can be injected using `@Configuration.Value`. Default values can be defined using annotations in `@Default` + +Services available for injection: + +- link:{config-javadoc-base-url}/io/helidon/config/Config.html[`io.helidon.config.Config`] + +Annotations: + +- link:{config-javadoc-base-url}/io/helidon/config/Configuration.html[`io.helidon.config.Configuration.Value`] - define the configuration key to inject, on constructor parameter +- Annotations defined in link:{common-javadoc-base-url}/io/helidon/common/Default.html[`io.helidon.common.Default`] - define a default typed value, on the same constructor parameter + +Example of usage can be seen below in HTTP Server Endpoint example. + +=== HTTP Server Endpoint [[Dec-HTTP-Server]] + +To create an HTTP endpoint, simply annotate a class with `@RestServer.Endpoint`, and +add at least one method annotated with one of the HTTP method annotations, such as `@Http.GET`. + +Services available for injection: + +N/A + +Supported method parameters (no annotation required): + +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/ServerRequest.html[`io.helidon.webserver.http.ServerRequest`] +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/ServerResponse.html[`io.helidon.webserver.http.ServerResponse`] +- link:{context-javadoc-base-url}/io/helidon/common/context/Context.html[`io.helidon.common.context.Context`] +- `io.helidon.common.security.SecurityContext` +- link:{security-javadoc-base-url}/io/helidon/security/SecurityContext.html`[`io.helidon.security.SecurityContext] - in case `helidon-security` module is on the classpath + +Annotations on endpoint type: + +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.Endpoint.html[`io.helidon.webserver.http.RestServer.Endpoint`] - required annotation +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.Listener.html[`io.helidon.webserver.http.RestServer.Listener`] - to define the named listener this should be served on (named port/socket) +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.Header.html[`io.helidon.webserver.http.RestServer.Header`] - header to return with each response from this endpoint +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.ComputedHeader.html[`io.helidon.webserver.http.RestServer.ComputedHeader`] - computed header to return with each response from this endpoint +- link:{http-javadoc-base-url}/io/helidon/http/Http.Path.html[`io.helidon.http.Http.Path`] - path (context) this endpoint will be available on + +Annotations on endpoint methods: + +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.Header.html[`io.helidon.webserver.http.RestServer.Header`] - header to return with each response from this method +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.ComputedHeader.html[`io.helidon.webserver.http.RestServer.ComputedHeader`] - computed header to return with each response from this method +- link:{webserver-javadoc-base-url}/io/helidon/webserver/http/RestServer.Status.html[`io.helidon.webserver.http.RestServer.Status`] - status to return (if a custom one is required) +- link:{http-javadoc-base-url}/io/helidon/http/Http.Path.html[`io.helidon.http.Http.Path`] - path (context) this method will be available on (subpath of the endpoint path) +- link:{http-javadoc-base-url}/io/helidon/http/Http.GET.html[`io.helidon.http.Http.GET`] (and other methods) - definition of HTTP method this method will serve +- link:{http-javadoc-base-url}/io/helidon/http/Http.HttpMethod.html[`io.helidon.http.Http.HttpMethod`] - for custom HTTP method names (mutually exclusive with above) +- link:{http-javadoc-base-url}/io/helidon/http/Http.Produces.html[`io.helidon.http.Http.Produces`] - what media type this method produces (return entity content type) +- link:{http-javadoc-base-url}/io/helidon/http/Http.Consumes.html[`io.helidon.http.Http.Consumes`] - what media type this method accepts (request entity content type) + +Annotations on method parameters: + +- link:{http-javadoc-base-url}/io/helidon/http/Http.Entity.html[`io.helidon.http.Http.Entity`] - Request entity, a typed parameter is expected, will use HTTP media type modules to coerce into the correct type +- link:{http-javadoc-base-url}/io/helidon/http/Http.HeaderParam.html[`io.helidon.http.Http.HeaderParam`] - Typed HTTP request header value +- link:{http-javadoc-base-url}/io/helidon/http/Http.QueryParam.html[`io.helidon.http.Http.QueryParam`] - Typed HTTP query value +- link:{http-javadoc-base-url}/io/helidon/http/Http.PathParam.html[`io.helidon.http.Http.PathParam`] - Typed parameter from path template + +[source,java] +.Example of an HTTP Server Endpoint +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_2, indent=0] +---- + +=== Typed HTTP Client [[Dec-HTTP-Client]] + +To create a typed HTTP client, create an interface annotated with `RestClient.Endpoint`, and at least one method annotated +with one fo the HTTP method annotations, such as `@Http.GET`. Methods can only have parameters annotated with one of the +`Http` qualifiers. + +Annotations on endpoint type: + +- link:{webclient-api-javadoc-base-url}/io/helidon/webclient/api/RestClient.Endpoint.html[`io.helidon.webclient.api.RestClient.Endpoint`] - required annotation +- link:{http-javadoc-base-url}/io/helidon/http/Http.Path.html[`io.helidon.http.Http.Path`] - path (context) the server listens on +- link:{webclient-api-javadoc-base-url}/io/helidon/webclient/api/RestClient.Header.html[`io.helidon.webclient.api.RestClient.Header`] - header to include in every request to the server +- link:{webclient-api-javadoc-base-url}/io/helidon/webclient/api/RestClient.ComputedHeader.html[`io.helidon.webclient.api.RestClient.ComputedHeader`] - header to compute and include in every request to the server + +Annotations on endpoint methods: + +- link:{webclient-api-javadoc-base-url}/io/helidon/webclient/api/RestClient.Header.html[`io.helidon.webclient.api.RestClient.Header`] - header to include in every request to the server +- link:{webclient-api-javadoc-base-url}/io/helidon/webclient/api/RestClient.ComputedHeader.html[`io.helidon.webclient.api.RestClient.ComputedHeader`] - header to compute and include in every request to the server +- link:{http-javadoc-base-url}/io/helidon/http/Http.Path.html[`io.helidon.http.Http.Path`] - path (context) the server serves this endpoint method on +- link:{http-javadoc-base-url}/io/helidon/http/Http.GET.html[`io.helidon.http.Http.GET`] (and other methods) - definition of HTTP method this method will invoke +- link:{http-javadoc-base-url}/io/helidon/http/Http.HttpMethod.html[`io.helidon.http.Http.HttpMethod`] - for custom HTTP method names (mutually exclusive with above) +- link:{http-javadoc-base-url}/io/helidon/http/Http.Produces.html[`io.helidon.http.Http.Produces`] - what media type this method produces (content type of entity from the server) +- link:{http-javadoc-base-url}/io/helidon/http/Http.Consumes.html[`io.helidon.http.Http.Consumes`] - what media type this method accepts (request entity content type) + +Annotations on method parameters: + +- link:{http-javadoc-base-url}/io/helidon/http/Http.Entity.html[`io.helidon.http.Http.Entity`] - Request entity, a typed parameter is expected, will use HTTP media type modules to write to the request +- link:{http-javadoc-base-url}/io/helidon/http/Http.HeaderParam.html[`io.helidon.http.Http.HeaderParam`] - Typed HTTP header value to send +- link:{http-javadoc-base-url}/io/helidon/http/Http.QueryParam.html[`io.helidon.http.Http.QueryParam`] - Typed HTTP query value to send +- link:{http-javadoc-base-url}/io/helidon/http/Http.PathParam.html[`io.helidon.http.Http.PathParam`] - Typed parameter from path template to construct the request URI + +[source,java] +.Example of a Typed HTTP Client +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_3, indent=0] +---- + + +=== Fault Tolerance [[Dec-FT]] + +Fault tolerance annotation allow adding features to methods on services. +The annotations can be added to any method that supports interception (i.e. methods that are not private). + +Method Annotations: + +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.Retry.html[`io.helidon.faulttolerance.Ft.Retry`] - allow retries +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.Fallback.html[`io.helidon.faulttolerance.Ft.Fallback`] - fallback to another method that provides +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.Async.html[`io.helidon.faulttolerance.Ft.Async`] - invoke method asynchronously +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.Timeout.html[`io.helidon.faulttolerance.Ft.Timeout`] - invoke method with a timeout +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.Bulkhead.html[`io.helidon.faulttolerance.Ft.Bulkhead`] - use bulkhead +- link:{faulttolerance-javadoc-base-url}/io/helidon/faulttolerance/Ft.CircuitBreaker.html[`io.helidon.faulttolerance.Ft.CircuitBreaker`] - use circuit breaker + +[source,java] +.Example of Fault Tolerance Fallback +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_4, indent=0] +---- + +=== Scheduling [[Dec-Scheduling]] + +Scheduling allows service methods to be invoked periodically. + +Method annotations: + +- link:{scheduling-se-javadoc-base-url}/io/helidon/scheduling/Scheduling.Cron.html[`io.helidon.scheduling.Scheduling.Cron`] - execute with schedule defined by a CRON expression +- link:{scheduling-se-javadoc-base-url}/io/helidon/scheduling/Scheduling.FixedRate.html[`io.helidon.scheduling.Scheduling.FixedRate`] - execute with a fixed interval + +[source,java] +.Example of a fixed rate scheduled method +---- +include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_5, indent=0] +---- diff --git a/docs/src/main/asciidoc/se/injection.adoc b/docs/src/main/asciidoc/se/injection/injection.adoc similarity index 83% rename from docs/src/main/asciidoc/se/injection.adoc rename to docs/src/main/asciidoc/se/injection/injection.adoc index f6cfa14c483..bef3ebd989c 100644 --- a/docs/src/main/asciidoc/se/injection.adoc +++ b/docs/src/main/asciidoc/se/injection/injection.adoc @@ -22,7 +22,7 @@ :keywords: helidon, se, injection :h1Prefix: SE :feature-name: Injection -:rootdir: {docdir}/.. +:rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] @@ -44,7 +44,6 @@ include::{rootdir}/includes/se.adoc[] - <> - <> - <> -- <> == Overview @@ -93,6 +92,8 @@ you declare that a class should be injectable using annotations like link:{service-registry-base-url}/io/helidon/service/registry/Service.Singleton.html[`@Service.Singleton`] (More about that later). +See xref:declarative.adoc#Overview[Helidon Declarative] + === Service registry A service registry is a tool that enables declarative programming by supporting inversion of control (IoC). @@ -823,7 +824,7 @@ must be shut down, once it is not needed. include::{sourcedir}/se/inject/ServiceRegistryExample.java[tag=snippet_2, indent=0] ---- -== Startup +== Startup [[generate-binding]] Helidon provides a Maven plugin (`io.helidon.service:helidon-service-maven-plugin`, goal `create-application`) to generate build time bindings, that can be used to start the service registry without any classpath discovery and reflection. @@ -906,189 +907,3 @@ level2 created level2 destroyed level1 destroyed ---- - -== Declarative - -Helidon declarative programming model allows inversion of control style programming with all the performance benefits of Helidon SE. - -Our declarative approach has the following advantages: - -- Uses Helidon SE imperative code to implement features (i.e. performance is same as "pure" imperative application) -- Generates all the necessary code at build-time, to avoid reflection and bytecode manipulation at runtime - -Helidon declarative is currently a preview feature, though we welcome feedback! - -To create a declarative application, use the annotations provided in our Helidon SE modules (details below), and the maven plugin -described above to generate the binding. -In addition, the following section must be added to the `build` of the Maven `pom.xml` to enable annotation processors that -generate the necessary code: - -[source,xml] ----- - - - org.apache.maven.plugins - maven-compiler-plugin - - - - io.helidon.bundles - helidon-bundles-apt - ${helidon.version} - - - - - ----- - -The following features are currently implemented: - -- <> -- <> -- <> -- <> -- <> - - -[source,java] -.Example of a declarative main class ----- -include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_1, indent=0] ----- - -=== Configuration [[Dec-Config]] - -Configuration can be injected as a whole into any service, or a specific configuration option can be injected using `@Configuration.Value`. Default values can be defined using annotations in `@Default` - -Services available for injection: - -- `io.helidon.config.Config` - -Annotations: - -- `io.helidon.config.Configuration.Value` - define the configuration key to inject, on constructor parameter -- `io.helidon.common.Default.*` - define a default typed value, on the same constructor parameter - -Example of usage can be seen below in HTTP Server Endpoint example. - -=== HTTP Server Endpoint [[Dec-HTTP-Server]] - -To create an HTTP endpoint, simply annotate a class with `@RestServer.Endpoint`, and -add at least one method annotated with one of the HTTP method annotations, such as `@Http.GET`. - -Services available for injection: - -N/A - -Supported method parameters (no annotation required): - -- `io.helidon.webserver.http.ServerRequest` -- `io.helidon.webserver.http.ServerResponse` -- `io.helidon.common.context.Context` -- `io.helidon.common.security.SecurityContext` -- `io.helidon.security.SecurityContext` - in case `helidon-security` module is on the classpath - -Annotations on endpoint type: - -- `io.helidon.webserver.http.RestServer.Endpoint` - required annotation -- `io.helidon.webserver.http.RestServer.Listener` - to define the named listener this should be served on (named port/socket) -- `io.helidon.webserver.http.RestServer.Header` - header to return with each response from this endpoint -- `io.helidon.webserver.http.RestServer.ComputedHeader` - computed header to return with each response from this endpoint -- `io.helidon.http.Http.Path` - path (context) this endpoint will be available on - -Annotations on endpoint methods: - -- `io.helidon.webserver.http.RestServer.Header` - header to return with each response from this method -- `io.helidon.webserver.http.RestServer.ComputedHeader` - computed header to return with each response from this method -- `io.helidon.webserver.http.RestServer.Status` - status to return (if a custom one is required) -- `io.helidon.http.Http.Path` - path (context) this method will be available on (subpath of the endpoint path) -- `io.helidon.http.Http.GET` (and other methods) - definition of HTTP method this method will serve -- `io.helidon.http.Http.HttpMethod` - for custom HTTP method names (mutually exclusive with above) -- `io.helidon.http.Http.Produces` - what media type this method produces (return entity content type) -- `io.helidon.http.Http.Consumes` - what media type this method accepts (request entity content type) - -Annotations on method parameters: - -- `io.helidon.http.Http.Entity` - Request entity, a typed parameter is expected, will use HTTP media type modules to coerce into the correct type -- `io.helidon.http.Http.HeaderParam` - Typed HTTP request header value -- `io.helidon.http.Http.QueryParam` - Typed HTTP query value -- `io.helidon.http.Http.PathParam` - Typed parameter from path template - -[source,java] -.Example of an HTTP Server Endpoint ----- -include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_2, indent=0] ----- - -=== Typed HTTP Client [[Dec-HTTP-Client]] - -To create a typed HTTP client, create an interface annotated with `RestClient.Endpoint`, and at least one method annotated -with one fo the HTTP method annotations, such as `@Http.GET`. Methods can only have parameters annotated with one of the -`Http` qualifiers. - -Annotations on endpoint type: - -- `io.helidon.webclient.api.RestClient.Endpoint` - required annotation -- `io.helidon.http.Http.Path` - path (context) the server listens on -- `io.helidon.webclient.api.RestClient.Header` - header to include in every request to the server -- `io.helidon.webclient.api.RestClient.ComputedHeader` - header to compute and include in every request to the server - -Annotations on endpoint methods: - -- `io.helidon.webclient.api.RestClient.Header` - header to include in every request to the server -- `io.helidon.webclient.api.RestClient.ComputedHeader` - header to compute and include in every request to the server -- `io.helidon.http.Http.Path` - path (context) the server serves this endpoint method on -- `io.helidon.http.Http.GET` (and other methods) - definition of HTTP method this method will invoke -- `io.helidon.http.Http.HttpMethod` - for custom HTTP method names (mutually exclusive with above) -- `io.helidon.http.Http.Produces` - what media type this method produces (content type of entity from the server) -- `io.helidon.http.Http.Consumes` - what media type this method accepts (request entity content type) - -Annotations on method parameters: - -- `io.helidon.http.Http.Entity` - Request entity, a typed parameter is expected, will use HTTP media type modules to write to the request -- `io.helidon.http.Http.HeaderParam` - Typed HTTP header value to send -- `io.helidon.http.Http.QueryParam` - Typed HTTP query value to send -- `io.helidon.http.Http.PathParam` - Typed parameter from path template to construct the request URI - -[source,java] -.Example of a Typed HTTP Client ----- -include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_3, indent=0] ----- - - -=== Fault Tolerance [[Dec-FT]] - -Fault tolerance annotation allow adding features to methods on services. -The annotations can be added to any method that supports interception (i.e. methods that are not private). - -Method Annotations: - -- `io.helidon.faulttolerance.Ft.Retry` - allow retries -- `io.helidon.faulttolerance.Ft.Fallback` - fallback to another method that provides -- `io.helidon.faulttolerance.Ft.Async` - invoke method asynchronously -- `io.helidon.faulttolerance.Ft.Timeout` - invoke method with a timeout -- `io.helidon.faulttolerance.Ft.Bulkhead` - use bulkhead -- `io.helidon.faulttolerance.Ft.CircuitBreaker` - use circuit breaker - -[source,java] -.Example of Fault Tolerance Fallback ----- -include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_4, indent=0] ----- - -=== Scheduling [[Dec-Scheduling]] - -Scheduling allows service methods to be invoked periodically. - -Method annotations: - -- `io.helidon.scheduling.Scheduling.Cron` - execute with schedule defined by a CRON expression -- `io.helidon.scheduling.Scheduling.FixedRate` - execute with a fixed interval - -[source,java] -.Example of a fixed rate scheduled method ----- -include::{sourcedir}/se/inject/DeclarativeExample.java[tag=snippet_5, indent=0] ----- diff --git a/docs/src/main/asciidoc/se/introduction.adoc b/docs/src/main/asciidoc/se/introduction.adoc index fe3c52a10ce..ab069ef1988 100644 --- a/docs/src/main/asciidoc/se/introduction.adoc +++ b/docs/src/main/asciidoc/se/introduction.adoc @@ -81,7 +81,7 @@ Expose health statuses of your applications. //Injection [CARD] .Injection -[icon=colorize,link=injection.adoc] +[icon=colorize,link=injection/injection.adoc] -- Use of the Helidon injection in your applications. -- diff --git a/docs/src/main/asciidoc/sitegen.yaml b/docs/src/main/asciidoc/sitegen.yaml index a7e77e07564..06fcd614f2a 100644 --- a/docs/src/main/asciidoc/sitegen.yaml +++ b/docs/src/main/asciidoc/sitegen.yaml @@ -443,12 +443,15 @@ backend: - "oci.adoc" - "hcv.adoc" - "neo4j.adoc" - - type: "PAGE" + - type: "MENU" title: "Injection" - source: "injection.adoc" + dir: "injection" glyph: type: "icon" value: "colorize" + sources: + - "injection.adoc" + - "declarative.adoc" - type: "MENU" title: "JSON-RPC" dir: "jsonrpc" diff --git a/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java b/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java index b5167c9d164..53d2bad04b5 100644 --- a/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java +++ b/docs/src/main/java/io/helidon/docs/se/inject/DeclarativeExample.java @@ -53,7 +53,7 @@ interface GreetClient { // end::snippet_3[] // tag::snippet_1[] - @Service.GenerateBinding + @Service.GenerateBinding // generated binding to bypass discovery and runtime binding public static class Main { public static void main(String[] args) { // configure logging @@ -66,20 +66,22 @@ public static void main(String[] args) { // end::snippet_1[] // tag::snippet_2[] - @RestServer.Endpoint - @Http.Path("/greet") - @Service.Singleton + @RestServer.Endpoint // identifies this class as a server endpoint + @Http.Path("/greet") // serve this endpoint on /greet context root (path) + @Service.Singleton // a singleton service (single instance within a service registry) static class GreetEndpoint { private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Map.of()); private final String greeting; + // inject app.greeting configuration value, use "Hello" if not configured GreetEndpoint(@Configuration.Value("app.greeting") @Default.Value("Hello") String greeting) { this.greeting = greeting; } - @Http.GET - @Http.Produces(MediaTypes.APPLICATION_JSON_VALUE) + @Http.GET // HTTP GET endpoint + @Http.Produces(MediaTypes.APPLICATION_JSON_VALUE) // produces entity of application/json media type public JsonObject getDefaultMessageHandler() { + // build the JSON object (requires `helidon-http-media-jsonp` on classpath) return JSON.createObjectBuilder() .add("message", greeting + " World!") .build(); @@ -96,6 +98,7 @@ String algorithm() throws IOException { return "some-algorithm"; } + // method that would be called if #algorithm fails with an IOException String fallbackAlgorithm() { return "default"; }