Skip to content

Commit 3588629

Browse files
committed
Quarkus integration via shared transport (ADR-0003 + ADR-0006)
Mounts Smithy operations on Quarkus's Vert.x HTTP server via a new upstream :server:server-vertx-bridge module — no separate Smithy listener. Replaces the experimental @produces Server shape with @produces Service. Upstream (smithy-java) * server-vertx-bridge: SmithyServiceBridge.bridge(services).bind(router) plus BoundBridge (unbind / shutdown) and BridgeOptions (path-prefix, workers, shutdown-grace). Routes are derived per operation; multi-protocol same-path requests (rpcv2-cbor + rpcv2-json on /service/X/operation/Y) collapse into one route that picks protocol by smithy-protocol header. * ServerProtocol.enumerateRoutes() + RouteSpec record. restJson1 uses @http(method, uri); rpcv2 computes /service/<Name>/operation/<Op> and skips services whose operations carry @http traits (avoids phantom mounts). * server-api package promoted out of @SmithyUnstableApi; bridge consumers now see a stable surface. * BasicServiceExample under examples/end-to-end demonstrating the bridge against plain Vert.x (no Quarkus). quarkus-smithy extension * SmithyVertxRecorder + SmithyProcessor mount the bridge on mainRouter at RUNTIME_INIT, register unbind/shutdown tasks, and short-circuit silently when no @produces Service beans exist (ADR-0006 OQ7). * SmithyServerConfig (@ConfigMapping prefix=quarkus.smithy.server) exposes path-prefix, workers, shutdown-grace. * UnremovableBeanBuildItem retains user @produces Service beans. examples/quarkus-server (canonical example) * Standalone Gradle build (its own gradlew) that consumes smithy-java only via mavenLocal — required because Quarkus workspace discovery would otherwise substitute sibling raw build/classes for json-codec's shaded jar and split the classloader graph. * Service-bean producer for CoffeeShop; full Dev & Test guide in README (boot, curl probes, hot reload, path-prefix, packaged jar, Dev UI, integration tests). Cleanup * Delete examples/quarkus-types and examples/quarkus-client. The extension is server-only today; Typed-client and Types-only are documented as future directions in README.md and CONTEXT.md but no longer ship as supported user-facing programming models. * Delete the experimental SmithyServerLifecycle observer (@produces Server path). * Drop the implementation-plan doc; ADR-0006 §Implementation is now the canonical record. * settings.gradle.kts no longer includes :examples:quarkus-{server, client,types} (server is intentionally standalone). Verification * :server:server-vertx-bridge:test 16/16 * :quarkus-smithy-integration-tests:test green * :aws:server:aws-server-restjson:integ + rpcv2 integ green * Prod jar smoke (java -jar quarkus-run.jar): GET /menu 200, PUT /order 200, GET /order/:id 200, GET /no-such 404 with correct payloads * Dev mode (quarkusDev): same four probes pass; hot reload works via BoundBridge.unbind()
1 parent 06a7e01 commit 3588629

57 files changed

Lines changed: 2960 additions & 1190 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

aws/server/aws-server-restjson/src/main/java/software/amazon/smithy/java/aws/server/restjson/AwsRestJson1Protocol.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import software.amazon.smithy.java.server.Service;
3333
import software.amazon.smithy.java.server.core.HttpJob;
3434
import software.amazon.smithy.java.server.core.Job;
35+
import software.amazon.smithy.java.server.core.RouteSpec;
3536
import software.amazon.smithy.java.server.core.ServerProtocol;
3637
import software.amazon.smithy.java.server.core.ServiceProtocolResolutionRequest;
3738
import software.amazon.smithy.java.server.core.ServiceProtocolResolutionResult;
@@ -47,11 +48,20 @@ final class AwsRestJson1Protocol extends ServerProtocol {
4748
private final Codec codec;
4849
private final Map<String, UriMatcherMap<Operation<?, ?>>> httpMethodToMatchersMap;
4950
private final HttpBinding httpBinding = new HttpBinding();
51+
private final List<RouteSpec> routes;
5052

5153
AwsRestJson1Protocol(List<Service> services) {
5254
super(services);
55+
var routesList = new java.util.ArrayList<RouteSpec>();
5356
var httpMethodToMatchers = new HashMap<String, UriMatcherMapBuilder<Operation<?, ?>>>();
5457
for (Service service : services) {
58+
// restJson1 only owns operations with an @http trait — the
59+
// inner-loop httpTrait check below naturally excludes
60+
// operations that belong to other protocols. Filtering by
61+
// service-level protocol trait was tried but doesn't work:
62+
// codegen-generated services may carry no traits on the
63+
// service schema (the protocol shows up only on operations
64+
// and members).
5565
for (var operation : service.getAllOperations()) {
5666
// Only process operations with HTTP trait.
5767
var httpTrait = operation.getApiOperation()
@@ -66,6 +76,7 @@ final class AwsRestJson1Protocol extends ServerProtocol {
6676
.toString();
6777
httpMethodToMatchers.computeIfAbsent(method, k -> UriTreeMatcherMap.builder())
6878
.add(UriPattern.forSpecificityRouting(pattern), operation);
79+
routesList.add(new RouteSpec(method, smithyToVertxPath(pattern), service, operation));
6980
}
7081
}
7182
this.httpMethodToMatchersMap = httpMethodToMatchers.entrySet()
@@ -75,6 +86,50 @@ final class AwsRestJson1Protocol extends ServerProtocol {
7586
.useJsonName(true)
7687
.useTimestampFormat(true)
7788
.build();
89+
this.routes = List.copyOf(routesList);
90+
}
91+
92+
@Override
93+
public List<RouteSpec> enumerateRoutes() {
94+
return routes;
95+
}
96+
97+
/**
98+
* Translate a Smithy URI pattern to Vert.x route syntax. Smithy uses
99+
* {@code /order/{id}} (and {@code /order/{id+}} for greedy labels);
100+
* Vert.x uses {@code /order/:id} (and {@code /order/*} for greedy
101+
* tails).
102+
*/
103+
private static String smithyToVertxPath(String smithyPattern) {
104+
// Strip query string portion if present; Vert.x routes match
105+
// path only, query handled by the underlying request.
106+
int qIdx = smithyPattern.indexOf('?');
107+
String path = qIdx >= 0 ? smithyPattern.substring(0, qIdx) : smithyPattern;
108+
StringBuilder out = new StringBuilder(path.length());
109+
int i = 0;
110+
while (i < path.length()) {
111+
char c = path.charAt(i);
112+
if (c == '{') {
113+
int end = path.indexOf('}', i);
114+
if (end < 0) {
115+
throw new IllegalArgumentException("Unterminated label in Smithy URI pattern: " + smithyPattern);
116+
}
117+
String label = path.substring(i + 1, end);
118+
if (label.endsWith("+")) {
119+
// Greedy label: Vert.x has no per-segment greedy syntax,
120+
// so use trailing wildcard. Any operation using a greedy
121+
// label must be the last segment of the pattern.
122+
out.append('*');
123+
} else {
124+
out.append(':').append(label);
125+
}
126+
i = end + 1;
127+
} else {
128+
out.append(c);
129+
i++;
130+
}
131+
}
132+
return out.toString();
78133
}
79134

80135
@Override

examples/end-to-end/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies {
1717

1818
// Server dependencies
1919
implementation("software.amazon.smithy.java:server-netty:$smithyJavaVersion")
20+
implementation("software.amazon.smithy.java:server-vertx-bridge:$smithyJavaVersion")
2021
implementation("software.amazon.smithy.java:aws-server-restjson:$smithyJavaVersion")
2122

2223
// Client dependencies
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.server.example;
7+
8+
import io.vertx.core.Vertx;
9+
import io.vertx.ext.web.Router;
10+
import software.amazon.smithy.java.example.etoe.service.CoffeeShop;
11+
import software.amazon.smithy.java.server.Service;
12+
import software.amazon.smithy.java.server.vertx.SmithyServiceBridge;
13+
14+
/**
15+
* Mirror of {@link BasicServerExample} for the Vert.x bridge. Returns
16+
* a built {@link Service} (the generated {@code CoffeeShop} stub) and
17+
* mounts it on a Vert.x {@link Router} via
18+
* {@link SmithyServiceBridge}.
19+
*
20+
* <p>This is the user-construction pattern that {@code quarkus-smithy}
21+
* users mirror in their {@code @Produces Service} producer methods —
22+
* see the {@code examples/quarkus-server} module.
23+
*/
24+
public class BasicServiceExample {
25+
26+
public static void main(String[] args) {
27+
Service coffeeShop = CoffeeShop.builder()
28+
.addCreateOrderOperation(new CreateOrder())
29+
.addGetMenuOperation(new GetMenu())
30+
.addGetOrderOperation(new GetOrder())
31+
.build();
32+
33+
Vertx vertx = Vertx.vertx();
34+
Router router = Router.router(vertx);
35+
36+
SmithyServiceBridge.bridge(java.util.List.of(coffeeShop)).bind(router);
37+
38+
vertx.createHttpServer()
39+
.requestHandler(router)
40+
.listen(8888)
41+
.toCompletionStage()
42+
.toCompletableFuture()
43+
.join();
44+
45+
System.out.println("Service mounted on http://localhost:8888");
46+
}
47+
}

examples/quarkus-client/README.md

Lines changed: 0 additions & 92 deletions
This file was deleted.

examples/quarkus-client/build.gradle.kts

Lines changed: 0 additions & 64 deletions
This file was deleted.

examples/quarkus-client/gradle.properties

Lines changed: 0 additions & 5 deletions
This file was deleted.

examples/quarkus-client/settings.gradle.kts

Lines changed: 0 additions & 22 deletions
This file was deleted.

examples/quarkus-client/smithy-build.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)