Quarkus integration via shared transport Vert.x and Smithy Codegen extension#1205
Open
chenliu0831 wants to merge 1 commit into
Open
Quarkus integration via shared transport Vert.x and Smithy Codegen extension#1205chenliu0831 wants to merge 1 commit into
chenliu0831 wants to merge 1 commit into
Conversation
chenliu0831
commented
May 22, 2026
Author
|
This is done now |
47c68e2 to
fbac6ab
Compare
chenliu0831
commented
May 23, 2026
| * {@code ProtocolResolver}'s static SPI cache (keyed on its own | ||
| * classloader) does not see runtime protocol jars under Quarkus's | ||
| * partitioned classloader hierarchy. | ||
| */ |
Author
There was a problem hiding this comment.
Not too sure about how Quarkus resolve those jars from extension.
Smithy services run inside Quarkus applications via a new extension that
mounts every CDI-discovered Service bean on Quarkus's main HTTP router.
Smithy operations share the Quarkus HTTP server's port — no separate
Smithy listener.
## Customer-facing surface
Users produce a `@Produces Service` bean (the generated service stub):
@ApplicationScoped
public class CoffeeShopServerConfig {
@produces @singleton
Service coffeeShop() {
return CoffeeShop.builder()
.addCreateOrderOperation(new CreateOrder())
.addGetMenuOperation(new GetMenu())
.addGetOrderOperation(new GetOrder())
.build();
}
}
The extension mounts a `SmithyVertxServer` on Quarkus's `Router` as a
single catch-all route. Per-request, a `ProtocolResolver` iterates a
precision-ordered list of `ServerProtocol`s and returns one of three
outcomes:
- claim -> dispatch to the operation
- no-claim -> ctx.next() (delegate to a sibling handler)
- claim-and-reject -> 404 directly (request is Smithy's but malformed)
This implements the Smithy 2.0 Wire-protocol-selection guide.
End-to-end CoffeeShop example at `examples/quarkus-server/`. Standalone
Gradle build that consumes smithy-java via mavenLocal — required because
including it as a subproject causes Quarkus dev-mode workspace discovery
to substitute sibling raw `build/classes` for the published jars,
splitting classloaders in ways that break `:codecs:json-codec`'s
shadowJar.
## Modules
- `:server:server-vertx` — `SmithyVertxServer implements
Handler<RoutingContext>`, `ServerOptions`, `VertxRequestHeaders`.
18 integration tests against a real Vert.x HTTP server.
- `:quarkus-smithy` (runtime) — `SmithyVertxRecorder` and
`SmithyServerConfig` (`@ConfigMapping` for
`quarkus.smithy.server.{path-prefix, workers, shutdown-grace}`).
The recorder collects `Service` beans from Arc, walks
TCCL+own-loader for `ServerProtocolProvider`s (required because
`ProtocolResolver`'s static SPI cache can't see runtime jars under
`QuarkusClassLoader`), constructs the server, mounts it on the
main router, and registers ordered shutdown tasks.
- `:quarkus-smithy-deployment` (build-time) — `SmithyProcessor`
`@BuildStep`s and `SmithyCodeGenProvider` that hooks Smithy
code generation into `quarkusGenerateCode` (no `smithy-base`
Gradle plugin needed).
- `:quarkus-smithy-integration-tests` — `SmithyCodeGenProviderTest`.
## Cross-module changes
- `:server:server-core`
- `ProtocolResolver` gains `resolveOrEmpty(...)` returning
`Optional<ServiceProtocolResolutionResult>` and a second ctor
accepting a pre-loaded protocol list (for callers like the
Quarkus recorder where the static SPI cache is blind).
- `HttpResponseSerializer` is new: status + header-copy +
content-type/length logic shared between Netty and the Vert.x
server. Body exposed as the underlying `DataStream` so Netty
preserves zero-copy via `Unpooled.wrappedBuffer(ByteBuffer)`.
- `ServerProtocolProvider.precision()` Javadoc documents the AWS
service-protocol scale (rpcv2Cbor=1 ... restXml=8).
- `:server:server-netty` — `HttpRequestHandler.writeResponse` adopts
`HttpResponseSerializer`. Behavior unchanged.
- Provider precision values: `RpcV2CborProtocolProvider` -> 1,
`RpcV2JsonProtocolProvider` -> 2, `AwsRestJson1ProtocolProvider`
-> 7. Previously all 0 (precision sort was a no-op against
classpath order).
- `:aws:server:aws-server-restjson` — drops dead `routes` field and
`smithyToVertxPath` helper (the Vert.x server no longer enumerates
per-operation routes).
## Verification
- `./gradlew :server:server-vertx:check` green. 19 integration tests
against a real Vert.x HTTP server: protocol resolution outcomes,
HTTP/2 round-trip, lifecycle, options, precision regression.
- `./gradlew :server:server-core:check` green. New tests:
`HttpResponseSerializerTest` (6) and `ProtocolResolverTest` (7).
- `./gradlew :quarkus-smithy:build :quarkus-smithy-deployment:build`
green (with `--no-configuration-cache` to work around a pre-existing
Quarkus extension-validation gradle-plugin issue).
- `examples/quarkus-server` end-to-end (`quarkusDev`):
`GET /menu`, `PUT /order`, `GET /order/<id>` all 200 under
restJson1; CoffeeShop also reachable via `POST
/service/CoffeeShop/operation/<Op>` with `smithy-protocol:
rpc-v2-cbor` and `smithy-protocol: rpc-v2-json` headers; rpcv2
path with no header -> Quarkus default 404 via ctx.next(); rpcv2
path with header but malformed URI -> server 404 with empty body
(claim-and-reject). The empty-body 404 vs Quarkus's default-page
404 confirms the resolution-outcome distinctions are observable.
## Known limitations (deferred)
- `@streaming Blob` operations not supported. The recorder installs
Vert.x's `BodyHandler` upstream of the server, fully buffering
request bodies before resolution runs.
- Native-image support is out of scope for this cut.
- CORS support on the Vert.x server (Netty has it).
- Cross-service `@http(uri)` collisions are silent at construction
time — the matcher's tie-break wins.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue #, if available: N/A, supersede #1196 since we decide to go single/unified server approach with shared transport first.
What
A Smithy-Java server that runs inside Quarkus. Users produce a @produces Service bean; operations mount on Quarkus's HTTP router as a single catch-all Vert.x route. Smithy operations share Quarkus's port.
Customer experience
End-to-end example:
examples/quarkus-server/(CoffeeShop). The README walks throughpublishToMavenLocal → quarkusDev → curl probes for restJson1, rpcv2Cbor, and rpcv2Json, plus a fall-through section that demonstrates the three resolution outcomes via observable 404 shapes.
Key abstractions to review
Reviewers can stop after these six classes and have the whole picture. Listed in the order a request flows through them.
:server:server-vertxSmithyVertxServer(server-vertx/.../SmithyVertxServer.java) — the single public type,where the whole module's behavior lives.
Handler<RoutingContext>that owns theProtocolResolver, orchestrator pool, and dispatchloop.
create(services, protocols, options)constructs;handle(rc)is the route handler.shutdown()drains workers — idempotent, returns the cached future.ServerOptions— two knobs:workerCountandpathPrefix. Intentionally narrow.interceptorfield (locked in by a tripwire test); noshutdownGrace(caller-bounded).VertxRequestHeaders— adapter from Vert.x'sMultiMapto Smithy'sHttpHeaders.MultiMap.size()delegation.:quarkus-smithy(runtime)SmithyVertxRecorder(quarkus-smithy/.../SmithyVertxRecorder.java) — Quarkus@Recorderinvoked at
RUNTIME_INIT.@Produces Servicebeans from Arc, walks TCCL+own-loader forServerProtocolProviders, buildsServerOptions, mounts the catch-all route, registersshutdown tasks.
ShutdownContextruns LIFO, soroute.remove()isregistered after
server.shutdown()to ensure it runs first.quarkus.smithy.server.shutdown-graceas a.get(grace, MS)deadline on theotherwise-unbounded server-shutdown future.
SmithyServerConfig—@ConfigMappingforquarkus.smithy.server.*.path-prefix,workers,shutdown-grace.:quarkus-smithy-deployment(build-time)SmithyProcessor(quarkus-smithy-deployment/.../SmithyProcessor.java) — the Quarkus@BuildStepglue.ServicebeansUnremovableso Arc keeps them even without a direct injection point.RUNTIME_INIT; falls back frommainRoutertohttpRouterwhenquarkus.http.root-path=/.SmithyCodeGenProvider(worth a glance) hooks Smithy code generation intoquarkusGenerateCode.E2E Tests
GET /menuPUT /orderbody{"coffeeType":"LATTE"}GET /order/<id>POST /service/CoffeeShop/operation/GetMenu+smithy-protocol: rpc-v2-cbor, body0xa0POST .../CreateOrder+ cbor header, body{coffeeType:"LATTE"}POST .../GetMenu+smithy-protocol: rpc-v2-json, body{}POST .../CreateOrder+ json header, body{coffeeType:"ESPRESSO"}POST /service/CoffeeShop/operation/GetMenuwithoutsmithy-protocolheadernext()POST /service/CoffeeShop/operation/withsmithy-protocol: rpc-v2-cbor(malformed URI)next()GET /q/notrealFollow-ups/Caveats
@streamingBlob operations.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.