Transmission #056: PDD planning for Jersey stub API
Started Prompt-Driven Development planning for running a Jersey server alongside Javalin, with OpenAPI-generated interfaces implemented as stubs (example-shaped responses, no data layer), ahead of client migration and future docker-compose integration tests.
Artifacts live under .agents/planning/2026-04-28-jersey-stub-api/ (rough-idea.md, idea-honing.md, and empty research/, design/, implementation/ for the rest of the PDD workflow).
PDD: requirements clarification started
Chose requirements clarification first. Logged Q1 in idea-honing.md (how clients reach Javalin vs Jersey during migration: ports, path prefix, config, etc.).
Q1 answered: Javalin on /api, Jersey on /v1 — path namespaces avoid caller-side conflict. Q2 added (single origin + routing vs two listeners / ports).
Q2 answered: Prefer one port; two ports acceptable if one-port integration is too complex. Q3 added (Jersey always on vs behind config; default when unset).
Q3 answered: Jersey stub always starts (single instance, no Javalin-only mode). Q4 added (stub body source: OpenAPI examples vs hand-built DTOs).
Q4 answered: Use generated models; arbitrary instances in Java are fine. Q5 added (auth parity for /v1 vs /api during stub phase).
Q5 answered: No auth; keep /v1 unauthenticated like /api for now. Q6 added (realistic 4xx vs always 2xx for stubs).
Q6 answered: Prefer 400 for invalid input; align with @Valid on generated API bodies; noted path-param / “unknown id” caveat. Q7 added (GET unknown id: 404 vs 400 vs 200 stub).
Q7 answered: 404 with empty body for not-found stub semantics. Q8 added (JSON parity with Javalin vs spec-only).
Q8 answered: /v1 JSON follows OpenAPI (generated contract), not Javalin wire quirks. Requirements clarification offered for closure in chat (Q1–Q8).
Research: Added research/existing-codebase-and-generator.md, research/jersey-runtime-and-single-port.md, research/stub-behavior-and-validation.md; noted openapi.yaml servers URL vs /v1 mount decision; 10 *Api interfaces; Grizzly vs single-port/proxy options; Bean Validation implementation gap.
PDD: detailed design
Added design/detailed-design.md (standalone: requirements table, architecture / sequence mermaid, components, data models, errors, testing, appendices). Awaits explicit go-ahead for implementation plan (implementation/plan.md).
PDD: implementation plan
Added implementation/plan.md with checklist and Steps 1–8 (deps → Grizzly + JERSEY_PORT → per-*Api stubs → OpenAPI/compose alignment). Default path uses two ports first; single-port deferred.
PDD: summary
Added summary.md (artifact index, short design/plan recap, next steps, refinement notes).
Jersey stub implementation (server)
Implemented the PDD plan in server/: Hibernate Validator + jersey-bean-validation, JerseyStubServer (Grizzly) on JERSEY_PORT (default 8082) with @ApplicationPath("v1"), Jackson + JsonNullableModule / JavaTimeModule via OpenApiObjectMapperProvider, ValidationFeature, and 10 *ApiStub classes under com.helerion.jersey.stub (GET-by-id 404 empty unless id == 1; CharactersApi: 201 create, @Valid events). App starts Jersey after Javalin and registers a shutdown hook. openapi.yaml servers → http://localhost:8082/v1; server/docker-compose.yml publishes 8082 and sets JERSEY_PORT; Dockerfile exposes 8082; README.md, AGENTS.md, .agents/summary/interfaces.md document JERSEY_PORT and the /v1 stub surface.
Docker: Jersey InjectionManagerFactory
Docker java -jar failed with InjectionManagerFactory not found because the fat JAR lacked HK2’s Jersey binding. Added org.glassfish.jersey.inject:jersey-hk2 (libs.jersey.hk2) so META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory is merged into the shadow JAR; Grizzly + Jersey should start in docker compose up again.
Docker: OffsetDateTime query params
After HK2, model validation failed with “No injection source” for @QueryParam OffsetDateTime (index 3 on listAchievements, listItems, etc.). Jersey has no default converter for java.time.OffsetDateTime. Added OffsetDateTimeParamConverterProvider (@Provider + ParamConverterProvider) and registered it on JerseyStubApplication so ISO-8601 query values parse correctly; blank/absent yields null.