Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOCS: 4.x Pico doc introductions #6572

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions docs/nima/builder.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
///////////////////////////////////////////////////////////////////////////////

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 Builder
:description: about Helidon Builder
:keywords: helidon, java, microservices, reactive, virtual threads, config, builder-config
ljamen marked this conversation as resolved.
Show resolved Hide resolved




== Contents

- <<Overview, Overview>>
- <<Maven Coordinates, Maven Coordinates>>
- <<Usage, Usage>>
- <<Configuration, Configuration>>
- <<Examples, Examples>>

//the purpose of this article is to introduce Helidon Builder and provide some high-level information about how and why its used. The examples and eventual Builder guide will provide the detailed steps.


== Overview

Helidon Builder is an annotation processor that code-generates fluent builder support just by adding `@Builder` to your declared Java types. This declarative style of using annotations helps you by reducing the amount of boilerplate [redundant?] code you need to write.
ljamen marked this conversation as resolved.
Show resolved Hide resolved

Specifically, Helidon Builder offers a rich set of capabilities, including:

* Extensibility: Helidon Builder is designed to be customized and extended in order to integrate directly with the Helidon Configuration system.

* Visitor Pattern: Helidon Builder allows every attribute in the @Builder-annotated target type to be visited for your general use.

* Validation: Having the visitor pattern available allows some convenient mix-in behaviors, one of which is checking for required attributes that must be set as part of the build() method of your target. By default, all non-Optional attributes are considered to be required attributes. Helidon Builder provides validation out of the box in the generated implementation classes.

* Interception: You can provide other custom semantics during the build() method (similar to validation) for other types of interception or decoration on your target builders.

* Packaging and usage choice: Helidon Builder can be used as a standalone module in your build as long as you are using Java 11 or later. When used in a standalone manner in your build, you can use the `requireLibraryDependencies()` attribute to toggle between supporting libraries (like validation). This allows you to either generate code directly into your application, or have it supplied by Helidon libraries. This gives you an opportunity to use Helidon's tooling in isolation in your project build, even if you don't plan to use Helidon in any other part of your application runtime.

//Do we want to say you don't plan to use Helidon???
ljamen marked this conversation as resolved.
Show resolved Hide resolved

== Maven Coordinates

To enable Builder, add the following dependencies and annotation processor path to your project's `pom.xml`:

.Builder dependencies
[source,java]

----
<dependency>
<groupId>io.helidon.builder</groupId>
<artifactId>helidon-builder</artifactId>
<version>4.0.0-ALPHA4</version>
ljamen marked this conversation as resolved.
Show resolved Hide resolved
</dependency>

----


.Annotation Processor Path
[source, java]

----
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.helidon.builder</groupId>
<artifactId>helidon-builder-processor</artifactId>
<version>4.0.0-ALPHA4</version>
ljamen marked this conversation as resolved.
Show resolved Hide resolved
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>


----

== Usage

Helidon Builder can be used in the following scenarios:

=== Coding and Testing

The following examples illustrate how to use Helidon Builder with simple test subject classes. To create your test subject classes, create an example with two interface types: `NameAndDescription` and `NameAndDescriptionAndComments` that extends from the first, each having the Builder annotation:

.NameAndDescription
[source, java]

----
@Builder(requireLibraryDependencies = false)
public interface NameAndDescription {
String name();
Optional<String> description();
}

----

.NameAndDescriptionAndComments
[source, java]

----
@Builder(requireLibraryDependencies = false)
public interface NameAndDescriptionAndComments extends NameAndDescription {
@Singular
List<String> comments();
}

----


NOTE: The two classes above use `requireLibraryDependencies=false` which allows you to use Maven's provided dependency for the Helidon Builder libraries. This also makes it possible to code-generate the supporting validation directly into your builder- generated types, thereby not requiring any Helidon library support in your runtime.


.Build Your Project
[source, java]

----
maven clean compile
----

.Write your main()
[source, java]

----
public class Main {
public static void main(String[] args) {
NameAndDescriptionAndComments target =
DefaultNameAndDescriptionAndComments.builder()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just reversed the code generation to make "Default" the suffix and not the prefix for generated code. So technically the name is NameAndDescriptionAndCommentsDefault

.name("world")
.addComment("hello")
.build();
System.out.println("Hello " + target);
}
}
----

.Output
[source, java]

----

Hello NameAndDescriptionAndComments(name=world, description=Optional.empty,
comments=[hello])

----



=== Auto-Generated Code

The Helidon Builder's annotation processor generates source code whenever it processes an annotation that it recognizes - in this case the @Builder annotation. You must apply the annotation processors in your build in order to generate the supporting sources before they are consumed in the main() from this example.




== Configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConfigBean Builder


//I wasn't sure how to position Builder Config. It seems like it fits in with Builder but perhaps we want to keep it separate? Also, are we using the term Pico Builder Config or something else?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConfigBean Builder is an extensibility feature over the base Builder to allow integration of the generated builder types to the Helidon Configuration subsystem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see you have it below.


Pico Builder Config is a specialization of Builder that extends the builder to support additional integration with Helidon's configuration subsystem. It adds support for the `@ConfigBean` annotation. When applied to a target interface it will map that interface to configuration via a new toBuilder method generated on the implementation as follows:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove Pico

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also make it toBuilder() as a code reference


.toBuilder method
[source,java]

----


public static Builder toBuilder(io.helidon.common.config.Config cfg) {

}



----

There are a few additional points to understand about ConfigBean and its supporting infrastructure:

* `@Builder` can be used in conjunction with `@ConfigBean`. All attributed will be honored exception one...
* `Builder.requireLibraryDependencies` is not supported. All generated configuration beans and builders will minimally require a compile-time and runtime dependency on Helidon's common-config module. But for full fidelity support of Helidon's config one should instead use the full config module.


== Examples

* See https://github.com/helidon-io/helidon/blob/4.0.0-ALPHA4/builder/tests/builder[tests/builder] for additional Helidon Builder usage examples.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these links should be relative to alpha6


* See https://github.com/helidon-io/helidon/tree/4.0.0-ALPHA4/pico/builder-config/tests[pico/builder-config/tests] for additional usage examples for Builder Configuration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove pico





100 changes: 92 additions & 8 deletions docs/nima/pico/pico_intro.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,22 @@

== Overview

Helidon Pico //explain what it is and what it can do.
Helidon Pico is an optional feature in Helidon that provides service registry, a lifecycle engine and extensibility for customized code generation. Foundationally, Pico provides a way to develop declarative code using standard Javax/Jakarta annotation types (not Helidon-specific). The Helidon Pico framework provides a mix of declarative and programmatic ways to build your application.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provides a service registry...


Pico's minimalist, compile-time generated dependency injection (DI) framework and compile-time source code generation has a number of advantages, including:

* pre-runtime validation of the dependency injection model
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First bullet should be:

  • It enables declarative, Inversion of Control style of programming.

* visibility into your application by providing "less magic" - understandability and debug-ability of your application
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offers visibility ..

* deterministic behavior (instead of depending on reflection and classpath ordering, etc.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provides a deterministic ...

* performance improvements since binding the model at compile-time is more efficient than computing it at runtime.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimizes performance ...






== Maven Coordinates

To enable Pico add the following dependencies to your project's `pom.xml` (see Managing Dependencies).
To enable Pico, add the following dependencies to your project's `pom.xml` (see Managing Dependencies).

.Pico dependencies
[source,java]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace the first with
helidon-pico-runtime

remove the second dependency on pico-types

Expand All @@ -60,13 +71,76 @@ To enable Pico add the following dependencies to your project's `pom.xml` (see M

== Usage

Once you have planned how each of your resources should support Pico, you specify the Pico behavior in one of two ways:
These are the basic steps to help you get started with using Helidon Pico.

NOTE: The Pico examples [link] provide detailed instructions for using Helidon Pico.

*Step 1: Update your pom.xml file*

Put the following dependencies in your `pom.xml` or `gradle.build` file:


.Builder dependencies
[source,java]
----

<groupId>io.helidon.pico</groupId>
<artifactId>helidon-pico-processor</artifactId>
<version>${helidon.version}</version>
----

.Compile-time dependency:
[source,java]
----

<dependency>
<groupId>io.helidon.pico</groupId>
<artifactId>helidon-pico-services</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

services -> runtime

<version>${helidon.version}</version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a better way to refer to versions so you don't need to constantly have APLHA4 -> ALPHA6, M1, etc issues to deal with.

</dependency>
----


*Step 2: Write your application*

Write your application using w/ standard jakarta.inject.* and jakarta.annotation.* types. Again, see any of the examples for pointers as needed.

*Step 3: Build and run*

In a DI-based framework, the frameworks "owns" the creation of services in accordance with the Scope each service is declared as. You therefore need to get things started by creating demand for the initial service(s) instead of ever calling new directly in your application code. Generally speaking, there are two such ways to get things started at runtime:

* If you know the class you want to create then look it up directly using the Services SPI. Here is a sample excerpt from the book example:

Services services = PicoServices.realizedServices();
// query
ServiceProvider<MyService> serviceProvider = services.lookupFirst(MyService.class);
// lazily activate
MyService myLazyActivatedService = serviceProvider.get();

* If there are a collection of services requiring activation at startup then we recommend annotating those service implementation types with `RunLevel(RunLevel.STARTUP)` and then use code below in `main()` to lazily activate those services.



* While Weight determines list order, the `RunLevel` annotation is used to rank the startup ordering, from the lowest value to the highest value, where `RunLevel.STARTUP == 0`. The developer is expected to activate these directly using code like the following (the get() lazily creates and activates the underlying service type):

List<ServiceProvider<Object>> startupServices = services
.lookup(DefaultServiceInfoCriteria.builder().runLevel(RunLevel.STARTUP).build());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default becomes the suffix.

startupServices.stream().forEach(ServiceProvider::get);



NOTE: Whenever list-based injection is used in Pico, all services matching the injection criteria will be in the injected (and immutable) list. The list will always be in order according to the Weight annotation value, ranking from the highest weight to the lowest weight. If services are not weighted explicitly, then a default weight is assigned. If the weight is the same for two services, then the secondary ordering will be based on the FN class name of the service types. **If the ordering of the list of services is important, remember to use the Weight and/or RunLevel annotations to establish the priority / weighted ordering, and startup ordering.**






== API

Adding Pico behavior to your Helidon Níma application involves just a few simple steps.
The Pico framework aims to provide a minimalist API implementation. As a result, it might be surprising to learn how small the actual API is for Pico. If you are already familiar with `jakarta.inject` and `jakarta.annotation`, then you are ready to go.



=== Maven Coordinates

Expand Down Expand Up @@ -101,16 +175,26 @@ The Pico API provides types that are generally useful at compile time to assign

== Configuration

Pico Config builder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pico Configuration should be instead referred to as "Pico Config-Driven Services"

Configuration can be achieved through a Pico add-on that is based on Helidon's configuration subsystem, and adds support for something called _config-driven services_ using the `@ConfiguredBy` annotation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not really "Configuration can be achieved".

Config-driven services is more about the second part of the sentence - where the presence of configuration in the Helidon Config subsystem will be injected directly into a service declared to be configured by that configuration type.


When applied to a target service interface, the `@ConfiguredBy` annotation will allow you to use a higher level aggregation for application configuration, and then allow the configuration to drive activation of services in the Pico Framework.

NOTE: You can use Pico configuration in combination with the Helidon configuration subsystem.

== Examples
There are a few additional caveats to understand about `ConfiguredBy` and its supporting infrastructure.

//optional
* `@ConfigBean Builder` is used to aggregate configuration attributes to this higher-level, application-centric configuration beans.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, this refers us back to the ConfigBean builder section - ideally with a link.


== Additional Information
* The Pico Framework needs to be started with the supporting configdriven modules in order for configuration to drive service activation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config-driven





== Examples
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When config-driven is used, then a different annotation processor should be used.

The basic pico service APT processor should use this one:

<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.10.1</version>
           <configuration>
               <annotationProcessorPaths>
                   <path>
                       <groupId>io.helidon.pico</groupId>
                       <artifactId>helidon-pico-processor</artifactId>
                       <version>${helidon.version}</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
       </plugin>
   </plugins>
</build>

whereas the Config-Driven one should use this (this one also covers the basic scenarios as well so you don't need both):

<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.10.1</version>
           <configuration>
               <annotationProcessorPaths>
                   <path>
                       <groupId>io.helidon.pico.configdriven</groupId>
                       <artifactId>helidon-pico-configdriven-processor</artifactId>
                       <version>${helidon.version}</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
       </plugin>
   </plugins>
</build>

For complete examples, see [link].

== Additional Information

//Closer to release we can provide links to blog articles, guides, etc.