avaje / avaje-jex

Web routing for the JDK Http server
Apache License 2.0
23 stars 1 forks source link
http-server java jdk-http-server jetty jvm web web-router

Supported JVM Versions Discord Build JDK EA License Maven Central javadoc

Avaje-Jex

Lightweight (~120KB) wrapper over the JDK's built-in HTTP server.

Features:

var app = Jex.create()
  .routing(routing -> routing
    .get("/", ctx -> ctx.text("hello"))
    .get("/one/{id}", ctx -> ctx.text("one-" + ctx.pathParam("id")))
    .filter(
        (ctx, chain) -> {
          System.out.println("before request");
          chain.proceed();
          System.out.println("after request");
        })
    .error(IllegalStateException.class, (ctx, exception) -> ctx.status(500).text("Handled IllegalStateException|" + exception.getMessage()))
  .staticResource(
        b ->
          b.httpPath("/myResource")
           .resource("/public")
           .directoryIndex("index.html"))
  .port(8080)
  .start();

Alternate HttpServer Implementations

The JDK provides an SPI to swap the underlying HttpServer, so you can easily use jex with alternate implementations by adding them as a dependency.

An example would be @robaho's implementation where performance seems to be increased by 10x over the default in certain benchmarks.

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-jex</artifactId>
  <version>3.0-RC8</version>
</dependency>

<dependency>
  <groupId>io.github.robaho</groupId>
  <artifactId>httpserver</artifactId>
  <version>1.0.10</version>
</dependency>

Use with Avaje Http

If you find yourself pining for the JAX-RS style of controllers, you can have avaje http generate jex adapters for your annotated classes.

Add dependencies

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-jex</artifactId>
  <version>3.0-RC8</version>
</dependency>

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-http-api</artifactId>
  <version>2.9-RC4</version>
</dependency>

<!-- Annotation processor -->
<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-http-jex-generator</artifactId>
  <version>2.9-RC4</version>
  <scope>provided</scope>
  <optional>true</optional>
</dependency>

JDK 23+

In JDK 23+, annotation processors are disabled by default, you will need to add a flag to re-enable.

<properties>
  <maven.compiler.proc>full</maven.compiler.proc>
</properties>

Define a Controller

package org.example.hello;

import io.avaje.http.api.Controller;
import io.avaje.http.api.Get;
import java.util.List;

@Controller("/widgets")
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()
  List<Widget> getAll() {
    return List.of(new Widget(1, "Rob"), new Widget(2, "Fi"));
  }

  record Widget(int id, String name){};
}

This will generate routing code that we can register using any JSR-330 compliant DI:

@Generated("avaje-jex-generator")
@Singleton
public class WidgetController$Route implements Routing.HttpService {

  private final WidgetController controller;

  public WidgetController$Route(WidgetController controller) {
    this.controller = controller;
  }

  @Override
  public void add(Routing routing) {
    routing.get("/widgets/{id}", this::_getById);
    routing.get("/widgets", this::_getAll);
  }

  private void _getById(Context ctx) throws IOException {
    ctx.status(200);
    var id = asInt(ctx.pathParam("id"));
    ctx.json(controller.getById(id));
  }

  private void _getAll(Context ctx) throws IOException {
    ctx.status(200);
    ctx.json(controller.getAll());
  }

}

JSR-330 DI Usage

You can use whatever DI library you like.

public class Main {

  public static void main(String[] args ) {

    List<Routing.HttpService> services = // Retrieve HttpServices via DI;
    Jex.create().routing(services).start();
  }
}

See also: