Skip to content

[JAVA] add JSpecify @Nullable in builder and singleRequestParameter - #24648

Open
jpfinne wants to merge 45 commits into
OpenAPITools:masterfrom
jpfinne:feature/jspecify_java_builder
Open

[JAVA] add JSpecify @Nullable in builder and singleRequestParameter#24648
jpfinne wants to merge 45 commits into
OpenAPITools:masterfrom
jpfinne:feature/jspecify_java_builder

Conversation

@jpfinne

@jpfinne jpfinne commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

When useJspecify=true, add the @Nullable annotation to the java builders.
Add @Nullable annotation to singleRequestParameters for restclient and webclient libraries.

Regenerate jspecify samples with more options to improve coverage.

  generateBuilders: true
  generateConstructorWithAllArgs: true
  useSingleRequestParameter: true
  useAbstractionForFiles: true

Tags added to jspecify sample -> multiple generate api classes. It covers more cases (like missing @Nullable import when all parameters are required.
Add sample for restClient+jspecify+openapiNullable

The builders are not modified when useJspecify=false. (it can be done in a # PR)

For simplicity, this PR does NOT cover. (it can be done in a # PR)

  • resttemplate api. The generated code has many warnings when adding @Nullable
  • ApiClient gives multiple jspecify warnings due to package being @NullMarked
  • some native client jspecify warnings.
  • spring boot: the samples are regenerated, but not fix done to new jspecify warnings

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Adds org.jspecify.annotations.Nullable to Java builder methods and API request parameters when useJspecify is enabled, including file params and arrays of files with useAbstractionForFiles. Regenerates jspecify samples with tag-split APIs, adds an uploadFiles example, and includes a Spring Boot 4 restclient sample with openApiNullable in CI.

  • New Features

    • Annotate builder parameters via Java/nullableArgument_builder.mustache.
    • Add Java/nullableArgumentForApi.mustache and use it in restclient/webclient for API params and single-request types, supporting org.springframework.core.io.Resource and Collection<Resource> when handling file params with useAbstractionForFiles.
    • Enable generateBuilders, all-args constructors, useSingleRequestParameter, useAbstractionForFiles, and useTags across native-jackson3-jspecify, restclient, resttemplate, and webclient.
    • Add restclient-springBoot4-jackson3-jspecify-openapiNullable sample (incl. uploadFiles) and include it in the JDK17 workflow.
  • Bug Fixes

    • Improve @Nullable parsing in AbstractJavaCodegen to handle a trailing space.
    • Adjust nullable_var_annotations.mustache spacing; fix webclient single-request toIndentedString.
    • Fix restclient API test/template to use correct file param types with useAbstractionForFiles; update tests for param annotations, tag-based API splits, array-of-file uploads; fix failing unit tests.

Written for commit 8df500c. Summary will update on new commits.

Review in cubic

jpfinne added 25 commits May 11, 2026 14:26
…ure/jspecify_java_builder

# Conflicts:
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
#	samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/model/Foo.java
#	samples/openapi3/server/petstore/springboot-4-jspecify/src/main/java/org/openapitools/api/FooApi.java
#	samples/openapi3/server/petstore/springboot-4-jspecify/src/main/java/org/openapitools/model/Foo.java
@jpfinne jpfinne changed the title [JAVA] add JSpecify @Nullable annotation to builder [JAVA] add JSpecify @Nullable annotation to builder and toIndentedString() Aug 8, 2026
@jpfinne
jpfinne marked this pull request as ready for review August 8, 2026 16:56

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/Java/nullable_var_annotations.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/nullable_var_annotations.mustache:1">
P2: `useJspecify` request-wrapper fields, constructors, accessors, and record components lose `@Nullable` when `useSingleRequestParameter` is enabled, since this lambda suppresses it without a following `jSpecifyDatatype`. Render those declarations via the nullable datatype/argument partial (or preserve the annotation in direct-use contexts).</violation>
</file>

<file name="samples/client/petstore/java/restclient-springBoot4-jackson3-jspecify-openapiNullable/src/main/java/org/openapitools/client/model/Foo.java">

<violation number="1" location="samples/client/petstore/java/restclient-springBoot4-jackson3-jspecify-openapiNullable/src/main/java/org/openapitools/client/model/Foo.java:29">
P3: The generated import block contains three duplicated imports (java.util.Arrays, org.openapitools.jackson.nullable.JsonNullable, com.fasterxml.jackson.annotation.JsonIgnore). Legal but untidy generated output; this sample adds two more duplicates than the sibling jspecify sample, so the new openapiNullable template path is emitting redundant imports. Remove the duplicates.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/Java/libraries/native/pojo.mustache Outdated
@jpfinne
jpfinne marked this pull request as draft August 8, 2026 18:01
@jpfinne
jpfinne marked this pull request as ready for review August 9, 2026 11:17

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 127 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/FileApi.java">

<violation number="1" location="samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/FileApi.java:154">
P2: A response header such as `filename=../outside` escapes the generated download directory and writes response content to an unintended filesystem location. Sanitize to a basename (or normalize and reject paths outside `tempDir`) in the native template, then regenerate samples.</violation>
</file>

<file name="samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/UploadApi.java">

<violation number="1" location="samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/UploadApi.java:262">
P1: Calling the documented nullable optional upload parameter with `null` throws before a request is sent because it is unconditionally converted to a binary multipart part. Guard the file part (and apply the same generation change in the native template) so an omitted file produces an empty multipart request.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


MultipartEntityBuilder multiPartBuilder = MultipartEntityBuilder.create();
boolean hasFiles = false;
multiPartBuilder.addBinaryBody("file", _file);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Calling the documented nullable optional upload parameter with null throws before a request is sent because it is unconditionally converted to a binary multipart part. Guard the file part (and apply the same generation change in the native template) so an omitted file produces an empty multipart request.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/UploadApi.java, line 262:

<comment>Calling the documented nullable optional upload parameter with `null` throws before a request is sent because it is unconditionally converted to a binary multipart part. Guard the file part (and apply the same generation change in the native template) so an omitted file produces an empty multipart request.</comment>

<file context>
@@ -0,0 +1,306 @@
+
+    MultipartEntityBuilder multiPartBuilder = MultipartEntityBuilder.create();
+    boolean hasFiles = false;
+    multiPartBuilder.addBinaryBody("file", _file);
+    hasFiles = true;
+    HttpEntity entity = multiPartBuilder.build();
</file context>

File file = null;
if (filename != null) {
java.nio.file.Path tempDir = java.nio.file.Files.createTempDirectory("swagger-gen-native");
java.nio.file.Path filePath = java.nio.file.Files.createFile(tempDir.resolve(filename));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A response header such as filename=../outside escapes the generated download directory and writes response content to an unintended filesystem location. Sanitize to a basename (or normalize and reject paths outside tempDir) in the native template, then regenerate samples.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/native-jackson3-jspecify/src/main/java/org/openapitools/client/api/FileApi.java, line 154:

<comment>A response header such as `filename=../outside` escapes the generated download directory and writes response content to an unintended filesystem location. Sanitize to a basename (or normalize and reject paths outside `tempDir`) in the native template, then regenerate samples.</comment>

<file context>
@@ -0,0 +1,269 @@
+    File file = null;
+    if (filename != null) {
+      java.nio.file.Path tempDir = java.nio.file.Files.createTempDirectory("swagger-gen-native");
+      java.nio.file.Path filePath = java.nio.file.Files.createFile(tempDir.resolve(filename));
+      file = filePath.toFile();
+      tempDir.toFile().deleteOnExit();   // best effort cleanup
</file context>

@jpfinne
jpfinne marked this pull request as draft August 9, 2026 12:37
@jpfinne jpfinne changed the title [JAVA] add JSpecify @Nullable annotation to builder [JAVA] add more JSpecify @Nullable (builder, resttemplate api) Aug 9, 2026
@jpfinne
jpfinne marked this pull request as ready for review August 9, 2026 18:02

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 157 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/Java/libraries/resttemplate/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/resttemplate/api.mustache:79">
P2: Optional abstracted file parameters remain non-null under `@NullMarked` although generated form handling accepts null; apply nullable/nonnull annotations to the `useAbstractionForFiles` branches too.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/Java/nullable-import.mustache Outdated
@jpfinne jpfinne changed the title [JAVA] add more JSpecify @Nullable (builder, resttemplate api) [JAVA] add JSpecify @Nullable in builder Aug 9, 2026
@jpfinne jpfinne changed the title [JAVA] add JSpecify @Nullable in builder [JAVA] add JSpecify @Nullable in builder and singleRequestParameter Aug 9, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/Java/libraries/restclient/single_request_parameter.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/restclient/single_request_parameter.mustache:5">
P2: Optional or nullable file parameters in `useJspecify=true,useAbstractionForFiles=true` request wrappers remain unannotated because the file branch bypasses `nullableArgument`. Apply the same nullable/type-use handling to the `Resource` and `Collection<Resource>` branches so builders meet the JSpecify contract for every parameter type.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/Java/libraries/webclient/single_request_parameter.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/webclient/single_request_parameter.mustache:6">
P2: Optional file parameters with `useAbstractionForFiles=true` still generate unannotated `Resource` request members, so this JSpecify request-parameter path loses nullable metadata. Route the literal Resource types through equivalent `jSpecifyNullable`/`jSpecifyDatatype` handling (while retaining existing non-JSpecify annotations).</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/Java/nullableArgumentForApi.mustache Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 34 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/openapi3/server/petstore/springboot-4-jspecify/src/main/java/org/openapitools/api/UploadFilesApi.java">

<violation number="1" location="samples/openapi3/server/petstore/springboot-4-jspecify/src/main/java/org/openapitools/api/UploadFilesApi.java:65">
P2: The optional `file` form parameter (required=false) is not annotated @Nullable, yet the file imports org.jspecify.annotations.Nullable (unused). Elsewhere in the same sample, optional params are marked @Nullable (e.g. FooApi `@RequestParam(value="dtQuery", required=false) java.time.@Nullable Instant dtQuery`), so under jspecify an absent multipart part is being typed as non-null, and the import is dead. The JavaSpring formParams.mustache isFile branch doesn't apply {{>nullableAnnotation}} to the file array param — add it there, then regenerate the samples.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

consumes = { "multipart/form-data" }
)
default ResponseEntity<Void> uploadFilesPost(
@Parameter(name = "file", description = "") @RequestPart(value = "file", required = false) List<MultipartFile> file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The optional file form parameter (required=false) is not annotated @nullable, yet the file imports org.jspecify.annotations.Nullable (unused). Elsewhere in the same sample, optional params are marked @nullable (e.g. FooApi @RequestParam(value="dtQuery", required=false) java.time.@Nullable Instant dtQuery), so under jspecify an absent multipart part is being typed as non-null, and the import is dead. The JavaSpring formParams.mustache isFile branch doesn't apply {{>nullableAnnotation}} to the file array param — add it there, then regenerate the samples.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/server/petstore/springboot-4-jspecify/src/main/java/org/openapitools/api/UploadFilesApi.java, line 65:

<comment>The optional `file` form parameter (required=false) is not annotated @Nullable, yet the file imports org.jspecify.annotations.Nullable (unused). Elsewhere in the same sample, optional params are marked @Nullable (e.g. FooApi `@RequestParam(value="dtQuery", required=false) java.time.@Nullable Instant dtQuery`), so under jspecify an absent multipart part is being typed as non-null, and the import is dead. The JavaSpring formParams.mustache isFile branch doesn't apply {{>nullableAnnotation}} to the file array param — add it there, then regenerate the samples.</comment>

<file context>
@@ -0,0 +1,71 @@
+        consumes = { "multipart/form-data" }
+    )
+    default ResponseEntity<Void> uploadFilesPost(
+        @Parameter(name = "file", description = "") @RequestPart(value = "file", required = false) List<MultipartFile> file
+    ) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
</file context>

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