Cette page définit le vocabulaire JAX-RS tel que Cassini l’implémente, et clarifie les apports de Jakarta REST 4.0 par rapport à JAX-RS 3.x — JPMS strict, SeBootstrap, virtual threads.

Vocabulaire JAX-RS

Resource

Classe annotée @Path. Cassini la matche par URI template (@Path("/users/{id}")) puis dispatche sur une resource method (@GET, @POST…​) ou une sub-resource locator (qui retourne une nouvelle ressource pour la suite du dispatch).

Resource method

Méthode annotée par un verbe HTTP — @GET, @POST, @PUT, @DELETE, @PATCH, @HEAD, @OPTIONS, ou @HttpMethod custom.

Provider

Composant @Provider qui étend le runtime : MessageBodyReader/Writer (sérialisation), ContextResolver (contexte injectable), ExceptionMapper (mappage exception → réponse), ParamConverter (conversion String ↔ type custom dans @QueryParam/@PathParam).

Filter

ContainerRequestFilter ou ContainerResponseFilter — intercepte la requête (entrante) ou la réponse (sortante). @PreMatching agit avant le routing.

Interceptor

ReaderInterceptor / WriterInterceptor — entoure la (dé)sérialisation du body.

Application

Sous-classe jakarta.ws.rs.core.Application qui déclare ressources et providers. Le racine HTTP est défini par @ApplicationPath ou par la Configuration SE-Bootstrap.

ParamConverter

ParamConverterProvider permet d’utiliser des types custom dans @PathParam / @QueryParam / @HeaderParam etc. — Cassini la résout au démarrage du stack.

Stack Cassini et transports

Cassini sépare strictement la couche REST (lecture des annotations, dispatch, content negotiation) du transport HTTP :

  • cassini-api — SPI publique (CassiniHttpExchange, CassiniHttpAdapter, CassiniStack, BeanProvider, ResourceFactory). Aucun symbole interne, aucune dépendance transport.

  • cassini-core — moteur JAX-RS (Invoker, ResourceScanner, MessageBodyRegistry). Strictement spec, agnostique du transport.

  • cassini-chappe — branche le moteur sur Chappe (HTTP/1.1, H2, virtual threads). Transport de référence du TCK.

  • cassini-jdk-http — fallback JDK 25 com.sun.net.httpserver (zéro dépendance externe, idéal pour smoke tests).

  • cassini-cdi-vauban — adapter BeanProvider qui résout les ressources via Vauban.

  • cassini-clientClientBuilder JAX-RS zéro-dépendance sur java.net.http + virtual threads (indépendant du serveur).

CassiniStack.builder() choisit le transport via ServiceLoader — le code applicatif ne référence aucun symbole transport.

Deux modes d’instanciation des ressources

  1. Mode A — sans CDI : new() via ResourceFactory.defaultFactory(). Utilisé par cassini-jdk-http standalone ou pour les démos. Ressources @Singleton ou per-request selon le scope déclaré.

  2. Mode B — DI via SPI BeanProvider : cassini-cdi-vauban ou tout autre adapter ServiceLoader. Aucun import jakarta.cdi dans cassini-api / cassini-core — découplage strict.

Routing : carte de Cassini

Comme la triangulation géodésique, le routing JAX-RS est hiérarchique et déterministe :

  1. Match littéral prioritaire sur le match regex ({id} puis \{id:\\d+}).

  2. Best-match sur le tuple (path, verb, media-type) — Request.selectVariant côté server, @Produces/@Consumes côté annotation.

  3. Sub-resource locator : la méthode retourne une instance, son @Path est consommé, le dispatch reprend depuis cette instance.

Différences REST 4.0 vs 3.x

JAX-RS 3.x

Jakarta REST 4.0 (Cassini)

Transport implicite (Servlet 4)

SeBootstrap natif §5.2, transport pluggable via RuntimeDelegate ServiceLoader

Threads plateforme

Virtual threads par défaut (Java 25), aucun pool plateforme nécessaire

Pas de support module-info officiel

JPMS natif : module-info.java complet, exports minimaux

Bootstrap limité au container

SeBootstrap.start(Application.class, config) standalone

Multipart partiel (Forms)

EntityPart §3.5.4 first-class

Records partiels

Records Java 17+ supportés natifs côté binding (Champollion)

Codegen statique

Cassini privilégie la génération à la compilation plutôt que la réflexion runtime. Par classe @Path, il génère deux artefacts résolus par nom :

  • <ResourceClass>$$CassiniAdapter — invocation directe de méthode + injection de champs + coercition @*Param inline (pas de Method.invoke, aucune réflexion par requête).

  • <ResourceClass>$$CassiniRoutes — la table de routes sous forme de littéraux, convertie en routes au démarrage sans scan d’annotations.

Trois niveaux les produisent, du plus préféré au fallback : APT (cassini-processor, à la compilation), le plugin Maven (cassini-maven-plugin, au build, pour les JARs de dépendances), et un générateur runtime Class-File API (fallback JVM-only). Les deux premiers sont compatibles AOT (GraalVM, Leyden CDS). Les providers MessageBodyReader/Writer s’enregistrent via META-INF/services ; l’ordre @Priority des filtres est résolu au build du stack, pas par requête.

Voir fonctionnement interne pour le pipeline complet et la réflexion résiduelle documentée.

Périmètre TCK : Core Profile / SE-Bootstrap

Cassini cible le profil Core / SE-Bootstrap : pas de Servlet côté serveur, pas de JAXB-runtime. Les tags TCK servlet et xml_binding sont exclus conformément au TCK Process 1.4.1 §5.2.3. Détails dans TCK.