From 50ecaa65b32e84396f1f3d1113759a05b1d98c74 Mon Sep 17 00:00:00 2001 From: Dmitry Aleksandrov Date: Tue, 17 Oct 2023 11:50:14 +0300 Subject: [PATCH] Add Global Config Signed-off-by: Dmitry Aleksandrov --- docs/se/guides/migration_4x.adoc | 242 ++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 3 deletions(-) diff --git a/docs/se/guides/migration_4x.adoc b/docs/se/guides/migration_4x.adoc index 4244c7b696e..c40a8ca4312 100644 --- a/docs/se/guides/migration_4x.adoc +++ b/docs/se/guides/migration_4x.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2024 Oracle and/or its affiliates. + 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. @@ -21,7 +21,7 @@ :keywords: helidon, porting, migration, upgrade, incompatibilities :rootdir: {docdir}/../.. -In Helidon 4.x we have made some major changes to Helidon. Reactive engine has been removed. APIS and implementations rewritten in "blocking" paradigm.. This guide will help you upgrade a Helidon MP 3.x application to 4.x. +In Helidon 4.x we have made some major changes to Helidon. Reactive engine has been removed. APIS and implementations are rewritten in "blocking" paradigm. This guide will help you upgrade a Helidon MP 3.x application to 4.x. == Java 21 Runtime @@ -31,5 +31,241 @@ Java 17 is no longer supported in Helidon 4. Java 21 or newer is required. Pleas Helidon 4 no more uses Netty. Helidon SE is now running on Helidon WebServer which is based on Virtual threads technology, available in Java 21. -== Deprecations +== Programming paradigm +Helidon SE has changed from an asynchronous style API to an imperative/blocking style API that is optimized for use with virtual threads. Currently, there is no compatibility API available + +== Server initialization and start up + +In Helidon 1.x-3.x to star a server, the following actions should be done: + +[source, java] +.Start Helidon SE 3.x Server +---- +static Single startServer() { + + Config config = Config.create(); + + WebServer server = WebServer.builder(createRouting(config)) + .config(config.get("server")) + .addMediaSupport(JsonpSupport.create()) + .build(); + + Single webserver = server.start(); <1> + + webserver.thenAccept(ws -> { <2> + System.out.println("WEB server is up! http://localhost:" + ws.port() + "/greet"); + ws.whenShutdown().thenRun(() -> System.out.println("WEB server is DOWN. Good bye!")); + }) + .exceptionallyAccept(t -> { <3> + System.err.println("Startup failed: " + t.getMessage()); + t.printStackTrace(System.err); + }); + + return webserver; +} +---- +<1> Server is started in an asynchronous way. A `Single` object is returned. +<2> Wait for the Server to start and print the message in an asynchronous way. +<3> Gracefully handle exceptions if they occur during the initialization process. + +Since Helidon SE in 3.x was reactive, during the start a `Single` object is returned, the server has been started in asynchronous way. We have to use reactive methods like `thenAccept` to wait for the server to start and then to perform the desired action. The exception handling should also be done in reactive way using the corresponding method. + +In Helidon 4.x there are no more Reactive APIs, so the Server startup is much simpler: + +[source, java] +.Start Helidon SE 4.x Server +---- +public static void main(String[] args) { + + Config config = Config.create(); + Config.global(config); + + WebServer server = WebServer.builder() <1> + .config(config.get("server")) + .routing(Main::routing) + .build() + .start(); <2> + + System.out.println("WEB server is up! http://localhost:" + server.port() + "/greet"); <3> +} +---- +<1> Configure the Server. +<2> Start the Server. No reactive objects returned. +<3> Print a message when the Server is started. + +Just create it, configure it, and wait for it to start. If any exceptions happen, they are handled the traditional way using available language constructions. + + +== Routing configuration + +In Helidon 1.x-3.x the routing config was done the following way: + +[source, java] +.Routing in Helidon SE 3.x Server +---- +private static Routing createRouting(Config config) { + + MetricsSupport metrics = MetricsSupport.create(); <1> + HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) + .build(); + + GreetService greetService = new GreetService(config); <2> + + return Routing.builder() + .register(health) <3> + .register(metrics) + .register("/greet", greetService) <4> + .build(); +} +---- +<1> Create and configure `Metrics` and `Heath` support. +<2> Create a regular Helidon Service. +<3> Register `Metrics` and `Heath` support as Helidon Services. +<4> Register the regular Greeting service. + +Services are created and assigned to the desired path. Observability and other features are being created as usual Helidon `services`, available as part of the framework. Uses defined services are also registered the same way. + +In Helidon 4, the routing is configured the following way: + +[source, java] +.Start Helidon SE 4.x Server +---- +static void routing(HttpRouting.Builder routing) { + Config config = Config.global(); + + routing.register("/greet", new GreetService()) <1> + .addFeature(OpenApiFeature.create(config.get("openapi"))) <2> + .addFeature(ObserveFeature.create(config.get("observe"))); +} +---- +<1> Register Greeting service as in previous versions of Helidon. +<2> Add `Feature` for Observability and OpenAPI. + + +`Feature` encapsulates a set of endpoints, services and/or filters. It is similar to `HttpService` but gives more freedom in setup. Main difference is that a feature can add `Filters` and it cannot be registered on a path. Features are not registered immediately—each feature can define a `Weight` or implement `Weighted` to order features according to their weight. Higher-weighted features are registered first. This is to allow ordering of features in a meaningful way (e.g. Context should be first, Tracing second, Security third etc.). + + +== Services + +There are also significant changes in Helidon `Service`. + +In prior versions, a service looks this way: + +[source, java] +.Helidon SE 3.x Service +---- +public class GreetService implements Service { + + @Override + public void update(Routing.Rules rules) { <1> + rules + .get("/", this::getDefaultMessageHandler) + .get("/{name}", this::getMessageHandler) + .put("/greeting", this::updateGreetingHandler); + } + + private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { <2> + sendResponse(response, "World"); + } + + // other methods omitted +} +---- +<1> Use `update()` method to set up routing. +<2> Handle a `Request` and return a `Responce`. + +In Helidon 4, the same service: + +[source, java] +.Helidon SE 4.x Service +---- +public class GreetService implements HttpService { <1> + + @Override + public void routing(HttpRules rules) { <2> + rules.get("/", this::getDefaultMessageHandler) + .get("/{name}", this::getMessageHandler) + .put("/greeting", this::updateGreetingHandler); + } + + + private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { <3> + sendResponse(response, "World"); + } + + // other methods omitted +} +---- +<1> Implement `HttpService` for the `GreetingService`. +<2> Use `routing(HttpRules rules)` to set up routing. +<3> Handle a `Request` and return a `Responce`. + +Helidon 4 introduced `HttpService` that should be implemented in order to process HTTP requests. To set up routing, the method `routing(HttpRules rules)` should now be used. It receives `HttpRules` object with routes description. + +These changes make Helidon 4 incompatible with previous versions. + +Learn more about `HttpService` and `Routing` at xref:../webserver.adoc[Helidon SE WebServer] + +=== Significant changes + +==== Testing + +There is a new testing framework for Helidon SE. + +[source, xml] +---- + + io.helidon.microprofile.testing + helidon-microprofile-testing-junit5 + test + +---- + +Find more information proceed to xref:../introduction.adoc[Helidon SE testing] + +==== Observability + +Observability features of Helidon have now moved to different package. For `Heath` and `Metrics` please use: + +[source, xml] +---- + + io.helidon.webserver.observe + helidon-webserver-observe-health + + + io.helidon.webserver.observe + helidon-webserver-observe-metrics + +---- + +Observability has new endpoints. + +For System Metrics, please use: + +[source, xml] +---- + + io.helidon.metrics + helidon-metrics-system-meters + +---- + +=== Global Configuration + +The global configuration represents a single instance of the `Config` class, which is implicitly employed by certain Helidon components. Furthermore, it offers a handy approach for your application to access configuration information from any part of your code. + +You can utilize the global configuration for easy retrieval of your application's configuration: + +``` +Config config = Config.global(); +``` + +More information at xref:../config/introduction.adoc[Helidon SE Config]. + + +== Conclusion + +Please proceed to xref:../introduction.adoc[Helidon SE Introduction] to find more information and documentation about each module.