Three minutes to serve a @Path resource over HTTP. Cassini is deployed via the standard SeBootstrap of the REST 4.0 spec — any Application class is started by a RuntimeDelegate provided by the active transport on the classpath.

Prerequisites

  • Java 25 + Maven 3.9.16.sdkmanrc at the repo root (sdk env to align).

  • The official Jakarta REST 4.0 TCK is required only for conformance — not for application development.

Maven coordinates

<dependencies>
  <dependency>
    <groupId>jakarta.ws.rs</groupId>
    <artifactId>jakarta.ws.rs-api</artifactId>
    <version>4.0.0</version>
  </dependency>
  <dependency>
    <groupId>io.vidocq.cassini</groupId>
    <artifactId>cassini-core</artifactId>
    <version>0.1.0-SNAPSHOT</version>
  </dependency>
  <!-- reference transport, embeds Chappe -->
  <dependency>
    <groupId>io.vidocq.cassini</groupId>
    <artifactId>cassini-chappe</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>runtime</scope>
  </dependency>
</dependencies>
For a zero-external-dependency deployment, swap cassini-chappe for cassini-jdk-http: it relies solely on the JDK’s com.sun.net.httpserver.

First resource

@Path("/hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello(@QueryParam("name") @DefaultValue("world") String name) {
        return "Hello, " + name + "!";
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Item getItem(@PathParam("id") long id) {
        return new Item(id, "Article #" + id);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response createItem(Item item) {
        return Response.status(Response.Status.CREATED).entity(item).build();
    }

    public record Item(long id, String label) {}
}

ChappeRuntimeDelegate (or JdkHttpRuntimeDelegate) is discovered via JPMS ServiceLoader — application code references no transport-specific symbol.

import jakarta.ws.rs.SeBootstrap;
import jakarta.ws.rs.core.Application;

public class Main {
    public static void main(String[] args) throws Exception {
        var config = SeBootstrap.Configuration.builder()
            .host("0.0.0.0")
            .port(8080)
            .rootPath("/")
            .build();

        SeBootstrap.start(MyApplication.class, config)
            .thenAccept(instance -> System.out.println(
                "Cassini started at http://localhost:" + instance.configuration().port()))
            .toCompletableFuture()
            .join();

        Thread.currentThread().join();
    }
}

class MyApplication extends Application {
    @Override public Set<Class<?>> getClasses() {
        return Set.of(HelloResource.class);
    }
}

Manual bootstrap via CassiniStack

For fine-grained control (embedding Cassini in an existing Chappe server, unit testing):

try (var stack = CassiniStack.builder()
        .application(new MyApplication())
        .port(8080)
        .build()) {
    stack.start();
    Thread.currentThread().join();
}

With CDI (Vauban)

Add cassini-cdi-vauban to the classpath. The Vauban BeanProvider is discovered via ServiceLoader; container-managed @Path / @Provider classes are scanned automatically.

@Path("/items")
@ApplicationScoped              // or @RequestScoped automatically via CassiniScopeExtension
public class ItemResource {

    @Inject ItemService service;

    @GET @Produces(MediaType.APPLICATION_JSON)
    public List<Item> list() { return service.findAll(); }
}
var container = VaubanContainer.builder()
        .addBeanClass(ItemService.class)
        .addBeanClass(ItemResource.class)
        .build();

SeBootstrap.start(new Application() {}, config);   // Empty Application: Vauban fills it

The CassiniScopeExtension (Build Compatible Extension) automatically adds @RequestScoped to @Path classes lacking an explicit scope — no extra annotation required.

Build and run

cd cassini
sdk env
./mvnw -ntp install -DskipTests
./mvnw -pl cassini-examples/cassini-examples-chappe -am exec:exec

Next: advanced use cases (sub-resources, providers, async, SSE), concepts, TCK status.