openapi-generator: [BUG] `java` and `spring` client generators: old `javax` classes are used, not compatible with Spring Boot 3 that migrated to `jakarta.*` classes

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What’s the actual output vs expected output?
Description

Modern Spring Boot 3 switched from javax.* annotations to jakarta.*. It leads to a problem — code generated with the OpenAPI Generator does not compile anymore.

Examples:

import javax.annotation.Generated; // incorrect annotation, it must be jakarta.annotation.Generated

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
@Validated
@Tag(name = "my-service", description = "Endpoints for My Service health-check")
public interface MyServiceApi {
import javax.servlet.http.HttpServletResponse; // it must be jakarta.servlet.http.HttpServletResponse
import java.io.IOException; // it must be jakarta.validation.constraints.*

public class ApiUtil {
    public static void setExampleResponse(NativeWebRequest req, String contentType, String example) {
import javax.validation.Valid; // it must be jakarta.validation.Valid
import javax.validation.constraints.*; 


    default ResponseEntity<Void> addEdamamInput(
        @Parameter(name = "EdamamData", description = "", required = true) @Valid @RequestBody List<EdamamData> edamamData
    ) {
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

openapi-generator version

The last released version is used:

        <openapi-generator-maven-plugin.version>6.2.1</openapi-generator-maven-plugin.version>
Generation Details

Maven config looks like this, tried both spring and java as generatorName.

                    <execution>
                        <id>ras-client</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/../etc/api/ras.yaml</inputSpec>
                            <generatorName>java</generatorName>
                            <library>resttemplate</library>

                            <configOptions>
                                <sourceFolder>src/main/java/main</sourceFolder>
                                <dateLibrary>java8</dateLibrary>
                                <openApiNullable>false</openApiNullable>
                                <packageName>my.generated.ras</packageName>
                                <apiPackage>my.generated.ras.api</apiPackage>
                                <modelPackage>my.generated.ras.model</modelPackage>
                                <hideGenerationTimestamp>true</hideGenerationTimestamp> <!-- does not change the package of javax.annotation.Generated -->
                            </configOptions>
                        </configuration>
                    </execution>

Steps to reproduce
Related issues/PRs

Yes, there are related issues, especially about javax.annotation.Generated. For example:

They are not fixed, specifying <hideGenerationTimestamp>true</hideGenerationTimestamp> just does not fill the timestamp, but the import is still import javax.annotation.Generated;.

Suggest a fix

We need a new generator that works for the new Spring version, which is planned to be released around 24.Nov.2022. It should use only the jakarta.* annotations instead of javax.* annotations.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 12
  • Comments: 19 (6 by maintainers)

Commits related to this issue

Most upvoted comments

It is possible to have jakarta.* imports with the following configOpts


<configOptions>
  <library>spring-boot</library>
  <oas3>true</oas3>
  <useSpringController>true</useSpringController>
  <!-- javax.* to jakarta.* -->
  <useSpringBoot3>true</useSpringBoot3>
  <useSpringfox>false</useSpringfox>
</configOptions>

@jwilmoth-nc In my case only jakarta annotations where produced which is desired:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>${openapi-generator-maven-plugin.version}</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
                <!-- See https://openapi-generator.tech/docs/generators/java/ for config options -->
                <generatorName>java</generatorName>
                <configOptions>
                    <apiPackage>...</apiPackage>
                    <modelPackage>...</modelPackage>
                    <sourceFolder>src/gen/java/main</sourceFolder>
                    <dateLibrary>java8</dateLibrary>
                    <java8>true</java8>
                    <library>native</library>
                    <openApiNullable>false</openApiNullable>
                    <useBeanValidation>true</useBeanValidation>
                    <interfaceOnly>true</interfaceOnly>
                    <useJakartaEe>true</useJakartaEe>
                </configOptions>
                <generateApis>false</generateApis>
                <generateApiTests>false</generateApiTests>
                <generateApiDocumentation>false</generateApiDocumentation>
                <generateModels>true</generateModels>
                <generateModelDocumentation>false</generateModelDocumentation>
                <generateModelTests>false</generateModelTests>
                <generateSupportingFiles>false</generateSupportingFiles>
            </configuration>
        </execution>
    </executions>
</plugin>

The client generator works for me with 6.3.0 and above (with useJakartaEe=true). Latest version is 6.6.0. Did you try that?

UPDATE: previously referred to snapshots - now not necessary.

For my locally built 6.3.0-SNAPSHOT the annotations are working (added <useJakartaEe>true</useJakartaEe> to each of the configs, both server, and client).

Will try to use the external maven repo now, thanks for the hint.

My server config example:

                    <execution>
                        <id>my-service-backend</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/../etc/api/my_service.yaml</inputSpec>

                            <generatorName>spring</generatorName>

                            <configOptions>
                                <sourceFolder>src/main/java/main</sourceFolder>
                                <interfaceOnly>true</interfaceOnly>
                                <dateLibrary>java8</dateLibrary>
                                <openApiNullable>false</openApiNullable>
                                <apiPackage>my.generated.rest</apiPackage>
                                <modelPackage>my.generated.rest.model</modelPackage>
                                <hideGenerationTimestamp>true</hideGenerationTimestamp>
                                <useJakartaEe>true</useJakartaEe>
                            </configOptions>
                            <typeMappings>
                                <typeMapping>Double=java.math.BigDecimal</typeMapping>
                            </typeMappings>
                        </configuration>
                    </execution>

My client config example:

                    <execution>
                        <id>my-client</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/../etc/api/my.yaml</inputSpec>
                            <generatorName>java</generatorName>
                            <library>resttemplate</library>

                            <configOptions>
                                <sourceFolder>src/main/java/main</sourceFolder>
                                <dateLibrary>java8</dateLibrary>
                                <openApiNullable>false</openApiNullable>
                                <packageName>my.generated.ras</packageName>
                                <apiPackage>my.generated.ras.api</apiPackage>
                                <modelPackage>my.generated.ras.model</modelPackage>
                                <hideGenerationTimestamp>true</hideGenerationTimestamp>
                                <useJakartaEe>true</useJakartaEe>
                            </configOptions>
                        </configuration>
                    </execution>

UPDATE: Yes, it’s working with the SonaType plugin repo:

        <pluginRepository>
            <id>sonatype</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>

At first, the question was about java generator (client), NOT ONLY the spring generator (server).

Well, for the server, the jakarta.* annotations are generated with spring generator with spring-boot library, yes.

However, there are no auth classes generated for the server generator.

So the question is — I need a client generator with jakarta.* annotations.

What for do I need this?

The problem arises for the clients of the external APIs.

To set the API key

For java generator with rest-template library, I set it like this (ApiClient and ApiKeyAuth are the generated classes):

    @PostConstruct
    private void setFdcApiKey() {
        final ApiClient apiClient = fdcApi.getApiClient();
        final ApiKeyAuth apiKeyAuth = (ApiKeyAuth) apiClient.getAuthentication(configProperty.getFdcApiClientKeyAuthentication());
        apiKeyAuth.setApiKey(configProperty.getFdcApiKey());
    }

To set the bearer token into the Authorization header

rasApi and apiClient are the generated classes

        this.apiClient.addDefaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + AuthenticationFilter.getBearerToken());
        this.rasApi.setApiClient(this.apiClient);