Trois minutes pour servir une ressource @Path en HTTP. Cassini se déploie via la SeBootstrap standard de la spec REST 4.0 — n’importe quelle classe Application est démarrée par un RuntimeDelegate fourni par le transport actif sur le classpath.

Prérequis

  • Java 25 + Maven 3.9.16 — fichier .sdkmanrc fourni à la racine du repo (sdk env pour aligner).

  • Le TCK officiel Jakarta REST 4.0 n’est requis que pour la conformance — pas pour développer une application.

Coordonnées Maven

<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>
  <!-- transport de référence, embarque Chappe -->
  <dependency>
    <groupId>io.vidocq.cassini</groupId>
    <artifactId>cassini-chappe</artifactId>
    <version>0.1.0-SNAPSHOT</version>
    <scope>runtime</scope>
  </dependency>
</dependencies>
Pour un déploiement zéro dépendance externe, utilisez cassini-jdk-http à la place de cassini-chappe : il s’appuie uniquement sur com.sun.net.httpserver du JDK.

Première ressource

@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) {}
}

Démarrage SE-Bootstrap (recommandé)

ChappeRuntimeDelegate (ou JdkHttpRuntimeDelegate) est découverte via ServiceLoader JPMS — le code applicatif ne référence aucun symbole transport.

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 démarré sur http://localhost:" + instance.configuration().port()))
            .toCompletableFuture()
            .join();

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

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

Bootstrap manuel via CassiniStack

Pour un contrôle fin (embarquer Cassini dans un serveur Chappe existant, ou tester unitairement) :

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

Avec CDI (Vauban)

Ajoutez cassini-cdi-vauban au classpath. Le BeanProvider Vauban est découvert via ServiceLoader ; les classes @Path / @Provider gérées par le container sont scannées automatiquement.

@Path("/items")
@ApplicationScoped              // ou @RequestScoped automatique 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);   // Application vide : Vauban remplit

Le CassiniScopeExtension (Build Compatible Extension) ajoute automatiquement @RequestScoped aux classes @Path sans scope explicite — aucune annotation supplémentaire requise.

Build et lancement

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

Étapes suivantes : cas d’usage avancés (sub-resources, providers, async, SSE), concepts, état TCK.