From 2a331c049c4e22d59eed4b1c525e2fbdb40f01af Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 3 Jan 2023 10:10:28 -0600 Subject: [PATCH 1/5] Bring the README more in line with the Docs --- README.md | 328 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 267 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 84359f626..377539d53 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,328 @@ -# avaje-http +# [Avaje-HTTP](https://avaje.io/http/) -Http server and client libraries and code generation. +HTTP server and client libraries via code generation. -## http server +## HTTP Server A jax-rs style controllers with annotations (`@Path`, `@Get` ...) that is lightweight by using source code generation (annotation processors) -to generate adapter code for Javalin and Helidon SE. +to generate adapter code for Javalin and Helidon SE/Nima. - Lightweight as in 65Kb library + generated source code -- Full use of Javalin or Helidon SE as desired +- Full use of Javalin or Helidon SE/Nima as desired +## Quick Start -## Define a Controller +#### 1. Add dependencies +```xml + + io.avaje + avaje-http-api + ${avaje.http.version} + +``` +#### 2. Add the generator module for your desired microframework as a annotation processor. + +```xml + + + io.avaje + avaje-inject-generator + ${avaje-inject.version} + provided + + + io.avaje + avaje-http-javalin-generator + ${avaje-http.version} + provided + +``` +If there are other annotation processors and they are specified via maven-compiler-plugin then we add avaje-http-generator there instead. +```xml + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.avaje + avaje-inject-generator + ${avaje-inject.version} + + + io.avaje + avaje-http-javalin-generator + ${avaje-http.version} + + + ... other annotation processor ... + + + + +``` +#### 3. Define a Controller (These APT processors work with both Java and Kotlin.) ```java -package org.example.hello +package org.example.hello; -import io.avaje.http.api.Controller -import io.avaje.http.api.Get -import io.avaje.http.api.Path +import io.avaje.http.api.Controller; +import io.avaje.http.api.Get; +import io.avaje.http.api.Path; +import java.util.List; @Path("/widgets") @Controller -class WidgetController(private val hello: HelloComponent) { +public class WidgetController { + private final HelloComponent hello; + public WidgetController(HelloComponent hello) { + this.hello = hello; + } + + @Get("/{id}") + Widget getById(int id) { + return new Widget(id, "you got it"+ hello.hello()); + } - @Get("/:id") - fun getById(id : Int): Widget { - return Widget(id, "you got it${hello.hello()}") + @Get() + List getAll() { + return List.of(new Widget(1, "Rob"), new Widget(2, "Fi")); } - @Get - fun getAll(): MutableList { + record Widget(int id, String name){}; +} +``` - val list = mutableListOf() - list.add(Widget(1, "Rob")) - list.add(Widget(2, "Fi")) +## Usage +The annotation processor will generate controller adapters that can register routes to Javalin/Helidon. The natural way to use the generated adapters is to get a DI library to find and wire them. This is what the below examples do and they use [Avaje-Inject](https://avaje.io/inject/) to do this. - return list - } +Note that there isn't a requirement to use Avaje for dependency injection. Any DI library that can find and wire the generated @Singleton beans can be used. You can even use Dagger2 or Guice to wire the controllers if you so desire. - data class Widget(var id: Int, var name: String) -} +### Usage with Javalin + +The annotation processor will generate controller classes implementing the WebRoutes interface, which means we can +get all the WebRoutes and register them with Javalin using: +```java +var routes = BeanScope.builder().build().list(WebRoutes.class); + +Javalin.create() + .routes(() -> routes.forEach(WebRoutes::registerRoutes)) + .start(); +``` + +### Usage with Helidon SE + +The annotation processor will generate controller classes implementing the Helidon Service interface, which we can use +get all the Services and register them with Helidon `RoutingBuilder`. + +```java +var routes = BeanScope.builder().build().list(Service.class); +var routingBuilder = Routing.builder().register(routes.stream().toArray(Service[]::new)); +WebServer.builder() + .addMediaSupport(JacksonSupport.create()) + .routing(routingBuilder) + .build() + .start(); ``` -## Generated source +### Usage with Helidon Nima + +The annotation processor will generate controller classes implementing the Helidon HttpService interface, which we can use +get all the services and register them with the Helidon `HttpRouting`. + +```java +var routes = BeanScope.builder().build().list(HttpService.class); +final var builder = HttpRouting.builder(); + +for (final HttpService httpService : routes) { + httpService.routing(builder); +} -The annotation processor will generate a `$Route` for the controller like below. +WebServer.builder() + .addRouting(builder.build()) + .build() + .start(); +``` +## Generated sources -Note that this class implements the WebRoutes interface, which means we can -get all the WebRoutes and register them with Javalin using. +### (Javalin) The generated WidgetController$Route.java is: ```java -fun main(args: Array) { +@Generated("avaje-javalin-generator") +@Component +public class WidgetController$Route implements WebRoutes { - // get all the webRoutes - val webRoutes = ApplicationScope.list(WebRoutes::class.java) + private final WidgetController controller; - val javalin = Javalin.create() + public WidgetController$Route(WidgetController controller) { + this.controller = controller; + } - javalin.routes { - // register all the routes with Javalin - webRoutes.forEach { it.registerRoutes() } + @Override + public void registerRoutes() { + + ApiBuilder.get("/widgets/{id}", ctx -> { + ctx.status(200); + var id = asInt(ctx.pathParam("id")); + var result = controller.getById(id); + ctx.json(result); + }); + + ApiBuilder.get("/widgets", ctx -> { + ctx.status(200); + var result = controller.getAll(); + ctx.json(result); + }); - // other routes etc as desired - ApiBuilder.get("/foo") { ctx -> - ctx.html("bar") - ctx.status(200) - } - ... } - javalin.start(7000) } - ``` -### The generated WidgetController$Route.java is: +### (Helidon SE) The generated WidgetController$Route.java is: +```java +@Generated("io.dinject.helidon-generator") +@Singleton +public class WidgetController$Route implements Service { + + private final WidgetController controller; + public WidgetController$Route(WidgetController controller) { + this.controller = controller; + } + + @Override + public void update(Routing.Rules rules) { + + rules.get("/widgets/{id}", this::_getById); + rules.post("/widgets", this::_getAll); + } + + private void _getById(ServerRequest req, ServerResponse res) { + int id = asInt(req.path().param("id")); + res.send(controller.getById(id)); + } + + private void _getAll(ServerRequest req, ServerResponse res) { + res.send(controller.getAll()); + } + +} +``` + +### (Helidon Nima) The generated WidgetController$Route.java is: ```java -package org.example.hello; +@Generated("avaje-helidon-nima-generator") +@Component +public class WidgetController$Route implements HttpService { -import static io.avaje.http.api.PathTypeConversion.*; -import io.avaje.http.api.WebRoutes; -import io.javalin.apibuilder.ApiBuilder; -import javax.annotation.Generated; -import javax.inject.Singleton; -import org.example.hello.WidgetController; + private final WidgetController controller; + public WidgetController$Route(WidgetController controller) { + this.controller = controller; + } -@Generated("io.avaje.javalin-generator") -@Singleton + @Override + public void routing(HttpRules rules) { + rules.get("/widgets/{id}", this::_getById); + rules.get("/widgets", this::_getAll); + } + + private void _getById(ServerRequest req, ServerResponse res) { + var pathParams = req.path().pathParameters(); + int id = asInt(pathParams.first("id").get()); + var result = controller.getById(id); + res.send(result); + } + + private void _getAll(ServerRequest req, ServerResponse res) { + var pathParams = req.path().pathParameters(); + var result = controller.getAll(); + res.send(result); + } + +} +``` + +## Generated sources ([Avaje-Jsonb](https://github.com/avaje/avaje-jsonb)) +If [Avaje-Jsonb](https://github.com/avaje/avaje-jsonb) is detected, http generators with support will use it for faster Json message processing. + +### (Javalin) The generated WidgetController$Route.java is: +```java +@Generated("avaje-javalin-generator") +@Component public class WidgetController$Route implements WebRoutes { - private final WidgetController controller; + private final WidgetController controller; + private final JsonType> listWidgetJsonType; + private final JsonType widgetJsonType; - public WidgetController$route(WidgetController controller) { - this.controller = controller; - } + public WidgetController$Route(WidgetController controller, Jsonb jsonB) { + this.controller = controller; + this.listWidgetJsonType = jsonB.type(Widget.class).list(); + this.widgetJsonType = jsonB.type(Widget.class); + } @Override public void registerRoutes() { - ApiBuilder.get("/widgets/:id", ctx -> { - int id = asInt(ctx.pathParam("id")); - ctx.json(controller.getById(id)); + ApiBuilder.get("/widgets/{id}", ctx -> { ctx.status(200); + var id = asInt(ctx.pathParam("id")); + var result = controller.getById(id); + widgetJsonType.toJson(result, ctx.contentType("application/json").outputStream()); }); ApiBuilder.get("/widgets", ctx -> { - ctx.json(controller.getAll()); ctx.status(200); + var result = controller.getAll(); + listWidgetJsonType.toJson(result, ctx.contentType("application/json").outputStream()); }); } + } ``` -Note that this APT processor works with both Java and Kotlin. +### (Helidon Nima) The generated WidgetController$Route.java is: + +```java +@Generated("avaje-helidon-nima-generator") +@Component +public class WidgetController$Route implements HttpService { + + + private final WidgetController controller; + private final JsonType widgetJsonType; + private final JsonType> listWidgetJsonType; + public WidgetController$Route(WidgetController controller, Jsonb jsonB) { + this.controller = controller; + this.widgetJsonType = jsonB.type(Widget.class); + this.listWidgetJsonType = jsonB.type(Widget.class).list(); + } + + @Override + public void routing(HttpRules rules) { + rules.get("/widgets/{id}", this::_getById); + rules.get("/widgets", this::_getAll); + } + + private void _getById(ServerRequest req, ServerResponse res) { + var pathParams = req.path().pathParameters(); + int id = asInt(pathParams.first("id").get()); + var result = controller.getById(id); + res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON); + widgetJsonType.toJson(result, res.outputStream()); + } + + private void _getAll(ServerRequest req, ServerResponse res) { + var pathParams = req.path().pathParameters(); + var result = controller.getAll(); + res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON); + listWidgetJsonType.toJson(result, res.outputStream()); + } +} +``` From 93eca1fe6e525c4f7f37ba0e3b37fd539de5b231 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 3 Jan 2023 10:14:38 -0600 Subject: [PATCH 2/5] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index aeafce8fb..3c9a96784 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,11 @@ to generate adapter code for Javalin and Helidon SE/Nima. #### 1. Add dependencies ```xml + + io.avaje + avaje-inject + ${avaje-inject.version} + io.avaje avaje-http-api From e8334d042fe8066d2755b078e553a0bc33c5846c Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 3 Jan 2023 10:17:40 -0600 Subject: [PATCH 3/5] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3c9a96784..379de37d7 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,7 @@ to generate adapter code for Javalin and Helidon SE/Nima. - Lightweight as in 65Kb library + generated source code - Full use of Javalin or Helidon SE/Nima as desired -## Quick Start - -#### 1. Add dependencies +## Add dependencies ```xml io.avaje @@ -26,7 +24,7 @@ to generate adapter code for Javalin and Helidon SE/Nima. ${avaje.http.version} ``` -#### 2. Add the generator module for your desired microframework as a annotation processor. +#### Add the generator module for your desired microframework as a annotation processor. ```xml @@ -67,7 +65,7 @@ If there are other annotation processors and they are specified via maven-com ``` -#### 3. Define a Controller (These APT processors work with both Java and Kotlin.) +## Define a Controller (These APT processors work with both Java and Kotlin.) ```java package org.example.hello; From 05833c69181fd0f80a02001867ba24680bd9a0de Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 3 Jan 2023 14:34:14 -0600 Subject: [PATCH 4/5] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 379de37d7..1aab095d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # [Avaje-HTTP](https://avaje.io/http/) +[![Build](https://github.com/avaje/avaje-http/actions/workflows/build.yml/badge.svg)](https://github.com/avaje/avaje-http/actions/workflows/build.yml) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/avaje/avaje-inject/blob/master/LICENSE) + HTTP server and client libraries via code generation. From e902d91336a4b1435f6d0c1fe3dec4fda58dcf14 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 3 Jan 2023 14:36:29 -0600 Subject: [PATCH 5/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aab095d1..60334e3a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [Avaje-HTTP](https://avaje.io/http/) [![Build](https://github.com/avaje/avaje-http/actions/workflows/build.yml/badge.svg)](https://github.com/avaje/avaje-http/actions/workflows/build.yml) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/avaje/avaje-inject/blob/master/LICENSE) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/avaje/avaje-inject/blob/master/LICENSE) HTTP server and client libraries via code generation.