Skip to content

Add gRPC-Web client codegen mirroring the OpenAPI pattern#5099

Open
shai-almog wants to merge 5 commits into
masterfrom
feat/grpc-client-codegen
Open

Add gRPC-Web client codegen mirroring the OpenAPI pattern#5099
shai-almog wants to merge 5 commits into
masterfrom
feat/grpc-client-codegen

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Adds a cn1:generate-grpc Maven goal that turns proto3 .proto files into hand-editable @ProtoMessage / @ProtoEnum / @GrpcClient sources, point-for-point matching the developer experience of the recently-added cn1:generate-openapi goal.
  • Adds the matching cn1-core runtime (com.codename1.io.grpc.*) and build-time annotation processors that emit binary protobuf codecs and gRPC-Web call sites into target/generated-sources so the project source stays clean.
  • Wire protocol is gRPC-Web binary (application/grpc-web+proto). Plain HTTP/2 gRPC needs trailers that ConnectionRequest doesn't expose; gRPC-Web is the standard mobile/browser variant that works with Envoy, the official grpcweb Go proxy, and the gRPC-Web filter shipped with modern gRPC server implementations.
  • Also closes a pre-existing gap where cn1app.RestClientBootstrap was generated by the OpenAPI processor but never spliced into Executor.annotationFrameworksInstallSource or JavaSEPort.postInit.

What lands where

Runtime (cn1-core):

  • com.codename1.annotations.grpc@GrpcClient, @Rpc, @ProtoMessage, @ProtoField, @ProtoEnum
  • com.codename1.io.grpcProtoWriter / ProtoReader / ProtoCodec / ProtoCodecs, GrpcWeb (framing + trailer parsing), GrpcResponse, GrpcException, GrpcClients registry

Maven plugin:

  • GenerateGrpcMojo — hand-rolled proto3 parser + emitter (no protoc dependency)
  • ProtoMessageAnnotationProcessor — emits per-class <T>ProtoCodec + cn1app.ProtoBootstrap
  • GrpcClientAnnotationProcessor — emits <Service>Impl + cn1app.GrpcClientBootstrap
  • Splice updates in Executor + JavaSEPort to install the three new bootstraps

Call site

GreeterGrpc g = GreeterGrpc.of(\"https://api.example.com\");
HelloRequest req = new HelloRequest();
req.name = \"world\";
g.sayHello(req, \"Bearer \" + token, response -> {
    if (response.isOk()) {
        renderGreeting(response.getResponseData().message);
    }
});

Scope of v1

  • Unary RPCs, proto3, all scalar types, nested messages, enums, repeated fields.
  • Streaming, map<K, V>, well-known types, and import are explicitly out — the parser errors cleanly on each.

Test plan

  • Plugin test suite: 143/143 pass (25 new tests for proto codec round-trips, gRPC-Web framing, the mojo's parser + emitter, and both annotation processors).
  • cn1-core compiles clean against -source 1.5 -target 1.5.
  • Manual smoke test: generate a Greeter client against helloworld.proto, point at an Envoy grpc-web proxy, run a unary RPC end-to-end from the simulator.
  • Manual smoke test: same client on iOS device (verify ParparVM translates the generated codec + impl cleanly).
  • Manual smoke test: same client on Android (verify R8 keeps the bootstrap reachable).

Docs

docs/developer-guide/appendix_goal_generate_grpc.adoc added and included in Maven-Appendix-Goals.adoc.

🤖 Generated with Claude Code

Generate typed gRPC clients from proto3 .proto files the same way
cn1:generate-openapi turns OpenAPI specs into REST clients: a Maven
goal emits user-edited @ProtoMessage / @ProtoEnum / @GrpcClient
sources into src/main/java, two build-time annotation processors
emit the protobuf codecs + gRPC-Web call sites into
target/generated-sources, and a pair of bootstrap classes plug
everything into the runtime registries before Display.init.

Wire protocol is gRPC-Web binary (application/grpc-web+proto) --
plain HTTP/2 gRPC requires trailers that ConnectionRequest does
not expose, but gRPC-Web is the standard mobile/browser variant
that works with Envoy, the official grpcweb Go proxy, and the
gRPC-Web filter shipped with modern gRPC server implementations.

Scope of v1: unary RPCs, proto3, all scalar types, nested
messages, enums, repeated fields. Streaming, map<K,V>,
well-known types, and `import` are out -- the parser errors
cleanly on each.

Also closes a pre-existing gap where cn1app.RestClientBootstrap
was generated by the OpenAPI processor but never spliced into the
Executor stub / JavaSEPort class-forname loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions
Copy link
Copy Markdown
Contributor

Cloudflare Preview

Ant build uses a CLDC11.jar bootclasspath that lacks Float
.floatToRawIntBits / Double.doubleToRawLongBits. Switch to the
plain floatToIntBits / doubleToLongBits variants -- the only
behavioural difference is that NaN bit patterns collapse to the
canonical NaN, which protobuf does not distinguish anyway.

Also: Vale Microsoft.Contractions wanted "doesn't" over "does not"
in the appendix; LanguageTool's English dictionary did not
recognise "protobuf", "varint", and "enums" so add them to the
developer-guide accept list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 29, 2026

Compared 122 screenshots: 122 matched.

Native Android coverage

  • 📊 Line coverage: 12.78% (7442/58229 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 10.41% (37323/358503), branch 4.31% (1460/33874), complexity 5.40% (1756/32544), method 9.43% (1438/15249), class 15.52% (330/2126)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 12.78% (7442/58229 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 10.41% (37323/358503), branch 4.31% (1460/33874), complexity 5.40% (1756/32544), method 9.43% (1438/15249), class 15.52% (330/2126)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 703.000 ms
Base64 CN1 encode 181.000 ms
Base64 encode ratio (CN1/native) 0.257x (74.3% faster)
Base64 native decode 1166.000 ms
Base64 CN1 decode 331.000 ms
Base64 decode ratio (CN1/native) 0.284x (71.6% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 29, 2026

Compared 11 screenshots: 11 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 29, 2026

Compared 122 screenshots: 122 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 179 seconds

Build and Run Timing

Metric Duration
Simulator Boot 67000 ms
Simulator Boot (Run) 2000 ms
App Install 15000 ms
App Launch 8000 ms
Test Execution 362000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 864.000 ms
Base64 CN1 encode 1566.000 ms
Base64 encode ratio (CN1/native) 1.813x (81.3% slower)
Base64 native decode 424.000 ms
Base64 CN1 decode 1518.000 ms
Base64 decode ratio (CN1/native) 3.580x (258.0% slower)
Base64 SIMD encode 482.000 ms
Base64 encode ratio (SIMD/native) 0.558x (44.2% faster)
Base64 encode ratio (SIMD/CN1) 0.308x (69.2% faster)
Base64 SIMD decode 614.000 ms
Base64 decode ratio (SIMD/native) 1.448x (44.8% slower)
Base64 decode ratio (SIMD/CN1) 0.404x (59.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 92.000 ms
Image createMask (SIMD on) 11.000 ms
Image createMask ratio (SIMD on/off) 0.120x (88.0% faster)
Image applyMask (SIMD off) 215.000 ms
Image applyMask (SIMD on) 97.000 ms
Image applyMask ratio (SIMD on/off) 0.451x (54.9% faster)
Image modifyAlpha (SIMD off) 321.000 ms
Image modifyAlpha (SIMD on) 73.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.227x (77.3% faster)
Image modifyAlpha removeColor (SIMD off) 323.000 ms
Image modifyAlpha removeColor (SIMD on) 170.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.526x (47.4% faster)
Image PNG encode (SIMD off) 1044.000 ms
Image PNG encode (SIMD on) 985.000 ms
Image PNG encode ratio (SIMD on/off) 0.943x (5.7% faster)
Image JPEG encode 872.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 29, 2026

Compared 122 screenshots: 122 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 351 seconds

Build and Run Timing

Metric Duration
Simulator Boot 130000 ms
Simulator Boot (Run) 1000 ms
App Install 52000 ms
App Launch 17000 ms
Test Execution 367000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1582.000 ms
Base64 CN1 encode 2647.000 ms
Base64 encode ratio (CN1/native) 1.673x (67.3% slower)
Base64 native decode 481.000 ms
Base64 CN1 decode 1915.000 ms
Base64 decode ratio (CN1/native) 3.981x (298.1% slower)
Base64 SIMD encode 622.000 ms
Base64 encode ratio (SIMD/native) 0.393x (60.7% faster)
Base64 encode ratio (SIMD/CN1) 0.235x (76.5% faster)
Base64 SIMD decode 656.000 ms
Base64 decode ratio (SIMD/native) 1.364x (36.4% slower)
Base64 decode ratio (SIMD/CN1) 0.343x (65.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 81.000 ms
Image createMask (SIMD on) 12.000 ms
Image createMask ratio (SIMD on/off) 0.148x (85.2% faster)
Image applyMask (SIMD off) 314.000 ms
Image applyMask (SIMD on) 139.000 ms
Image applyMask ratio (SIMD on/off) 0.443x (55.7% faster)
Image modifyAlpha (SIMD off) 362.000 ms
Image modifyAlpha (SIMD on) 346.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.956x (4.4% faster)
Image modifyAlpha removeColor (SIMD off) 677.000 ms
Image modifyAlpha removeColor (SIMD on) 338.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.499x (50.1% faster)
Image PNG encode (SIMD off) 2288.000 ms
Image PNG encode (SIMD on) 1489.000 ms
Image PNG encode ratio (SIMD on/off) 0.651x (34.9% faster)
Image JPEG encode 852.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 29, 2026

Compared 47 screenshots: 47 matched.
✅ JavaScript-port screenshot tests passed.

shai-almog and others added 3 commits May 29, 2026 22:19
The inner GrpcConnection extends ConnectionRequest and adds state
(response codec, callback, failure flags). SpotBugs flagged the
missing equals/hashCode override; match the pattern used by
RequestBuilder.Connection (delegate to super then compare the new
fields).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PMD's ControlStatementBraces rule rejects every `if (cond) stmt;`
style one-liner; brace the body so the rule passes. 28 sites
across ProtoWriter / ProtoReader / GrpcWeb. No behaviour change.

Also drop `public` from `WireKind` enum nested inside the
`@ProtoField` annotation -- members of an annotation type are
implicitly public, so PMD's UnnecessaryModifier rule flags the
qualifier as redundant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Checkstyle requires `{` to be followed by a line break, which the
previous round of PMD ControlStatementBraces fixes violated by
braceing the same-line form (`if (cond) { stmt; }`). Expand each
flagged site to the canonical multi-line shape:

    if (cond) {
        stmt;
    }

28 sites across ProtoWriter / ProtoReader / GrpcWeb; no behaviour
change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant