Skip to content

[Java][native] support InputStream mappings for binary payloads#24295

Draft
wingwogus wants to merge 4 commits into
OpenAPITools:masterfrom
wingwogus:fix/java-native-inputstream-24057
Draft

[Java][native] support InputStream mappings for binary payloads#24295
wingwogus wants to merge 4 commits into
OpenAPITools:masterfrom
wingwogus:fix/java-native-inputstream-24057

Conversation

@wingwogus

@wingwogus wingwogus commented Jul 14, 2026

Copy link
Copy Markdown

Fixes #24057.

Summary

  • Honor explicit file=InputStream and binary=InputStream mappings in the Java native client for binary responses and raw request bodies.
  • Return mapped response streams directly instead of routing them through the temporary-file branch, while preserving caller ownership in synchronous and asynchronous APIs.
  • Publish raw InputStream request bodies without Jackson serialization, including the gzip path.
  • Keep the default File behavior and JSON serialization/deserialization unchanged.

Implementation notes

The generator derives native-only template flags after type resolution. The stream path is selected only when the original OpenAPI schema is binary/file and the resolved Java type is java.io.InputStream; mapping an ordinary JSON schema to InputStream therefore continues to use Jackson.

The native templates transfer response-stream ownership before their cleanup blocks run. Async delivery also propagates cancellation to the transport and closes a stream that loses a delivery race. Request publishers are one-shot and thread-safe, including gzip, so a retry cannot read a consumed stream or double-close the normal source path.

@bbdouglas, this touches the Java 11 native HTTP client templates.

Verification

  • Focused generator/runtime scope: 9 tests, 0 failures/errors.
  • Generated and packaged fresh synchronous, asynchronous, and gzip-enabled Java native clients.
  • Verified the default File path and a non-binary JSON mapping remain on their existing branches.
  • checkstyle:check passed for modules/openapi-generator.
  • git diff --check passed.

The full repository test matrix was not run locally; CI is expected to provide the broader generator/sample coverage.

PR checklist

  • Read the contribution guidelines.
  • Run the full repository build/sample/doc regeneration commands from the PR template. Targeted generator tests, generated-client packages, and checkstyle were run as documented above; this change has no checked-in sample delta.
  • Mention the relevant technical committee member.

Summary by cubic

Adds support for explicit file=InputStream and binary=InputStream mappings in the Java native client, streaming binary responses and raw request bodies as java.io.InputStream instead of temp files or JSON. Keeps default File downloads and JSON handling unchanged; fixes #24057.

  • New Features
    • Return java.io.InputStream responses directly when the schema is binary/file and the resolved type is java.io.InputStream; transfer ownership to the caller and close undeliverable streams on async cancellation.
    • Publish InputStream request bodies directly (plain and gzip) via one‑shot, thread‑safe publishers; skip Jackson serialization.
    • Non-binary schemas mapped to InputStream keep normal JSON serialization/deserialization.

Written for commit a5b4028. Summary will update on new commits.

Review in cubic

Java native generation previously kept File-oriented response handling and Jackson request serialization even when file and binary types were explicitly mapped to java.io.InputStream. Mark only resolved native operations, publish mapped request streams directly, and transfer returned response-stream ownership past existing finally cleanup.

Constraint: Streaming behavior must be driven by the resolved java.io.InputStream mapping and operation.bodyParam, not isFile/isBinary flags or a new option.
Constraint: Existing native File downloads, JSON serialization, gzip wrapping, and finally cleanup must remain intact.
Rejected: Infer streaming from isFile/isBinary | would change default File behavior independently of configured mappings.
Confidence: high
Scope-risk: narrow
Directive: Keep response ownership transfer paired with nulling localVarResponseBody before the existing finally block.
Tested: JavaClientCodegenTest issue24057 sync/async/control cases plus native empty-response sync/async neighbors.
Tested: Generated sync and async native Maven clients packaged with file/binary=InputStream and InputStream=java.io.InputStream mappings.
Not-tested: Full repository test suite.
Native raw-stream ownership is valid only when the OpenAPI operation remains classified as binary or file and its resolved Java type is InputStream. Preserve normal Jackson request and response handling for non-binary schemas that users map to InputStream.

Constraint: Raw stream paths require both original binary/file metadata and an exact java.io.InputStream resolution
Constraint: The existing native template behavior remains unchanged
Rejected: Gate only on resolved InputStream type | mapped JSON incorrectly bypasses serialization
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Do not remove the binary/file metadata guard without testing mapped JSON request and response schemas
Tested: Genuine RED/GREEN regression; 6 focused issue and neighbor tests; fresh sync and async generated clients packaged with Maven
Not-tested: Full repository suite and live HTTP integration
Native InputStream responses need an explicit delivery owner because CompletableFuture cancellation does not reliably stop an already-running dependent action. Bridge both async public surfaces, close results that lose delivery races, and propagate cancellation only to the HTTP transport so processing can perform cleanup. Request streams are atomically one-shot for plain and gzip publishers, and gzip cleanup is idempotent after EOF.

Constraint: JDK BodyPublishers.ofInputStream may request a fresh stream for repeated subscriptions; the existing API accepts one caller-owned InputStream, not a reopenable supplier.
Rejected: Reuse the same request InputStream on retry | a consumed or closed stream can silently publish empty data.
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Preserve explicit async delivery bridges; cancelling the processing future can bypass undeliverable-stream cleanup.
Tested: Focused JavaClientCodegenTest issue/neighbor scope 9/9; deterministic async ownership and plain/gzip replay runtime harness; gzip exact-close RED/GREEN; generated sync, async, and gzip Maven packages.
Not-tested: Full repository test suite; live network redirect/retry behavior.
The cancellation runtime test previously observed cleanup through the inner WithHttpInfo bridge, leaving the convenience future's own lost-delivery branch unexecuted. Encapsulate convenience delivery in its returned future and deterministically invoke that exact path after cancellation with a close-tracking stream.

Constraint: The normal cancellation path also cancels WithHttpInfo, so the outer complete(false) race cannot be isolated by transport timing alone.
Rejected: Rely on source assertions for the outer branch | they do not execute ownership cleanup.
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep convenience response delivery routed through completeResponse so its undeliverable-stream close remains runtime-testable.
Tested: Isolated async ownership runtime test RED on missing delivery method, then GREEN with tests=1 failures=0 errors=0; generated source compiled dynamically at Java 11 release.
Not-tested: Full repository test suite; live network transport.
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.

[BUG][Java][Native] invalid generated code when binary/file responses are mapped to InputStream

1 participant