Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java/Microprofile] Add support for Jackson serialization & async #11554

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/generators/java.md
Expand Up @@ -56,10 +56,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|implicitHeadersRegex|Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true| |null|
|invokerPackage|root package for generated code| |org.openapitools.client|
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|library|library template (sub-template) to use|<dl><dt>**jersey1**</dt><dd>HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.9.x. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey3' or other HTTP libraries instead.</dd><dt>**jersey2**</dt><dd>HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.9.x</dd><dt>**jersey3**</dt><dd>HTTP client: Jersey client 3.x. JSON processing: Jackson 2.x</dd><dt>**feign**</dt><dd>HTTP client: OpenFeign 10.x. JSON processing: Jackson 2.9.x. or Gson 2.x</dd><dt>**okhttp-gson**</dt><dd>[DEFAULT] HTTP client: OkHttp 3.x. JSON processing: Gson 2.8.x. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.</dd><dt>**retrofit2**</dt><dd>HTTP client: OkHttp 3.x. JSON processing: Gson 2.x (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)</dd><dt>**resttemplate**</dt><dd>HTTP client: Spring RestTemplate 4.x. JSON processing: Jackson 2.9.x</dd><dt>**webclient**</dt><dd>HTTP client: Spring WebClient 5.x. JSON processing: Jackson 2.9.x</dd><dt>**resteasy**</dt><dd>HTTP client: Resteasy client 3.x. JSON processing: Jackson 2.9.x</dd><dt>**vertx**</dt><dd>HTTP client: VertX client 3.x. JSON processing: Jackson 2.9.x</dd><dt>**google-api-client**</dt><dd>HTTP client: Google API client 1.x. JSON processing: Jackson 2.9.x</dd><dt>**rest-assured**</dt><dd>HTTP client: rest-assured : 4.x. JSON processing: Gson 2.x or Jackson 2.10.x. Only for Java 8</dd><dt>**native**</dt><dd>HTTP client: Java native HttpClient. JSON processing: Jackson 2.9.x. Only for Java11+</dd><dt>**microprofile**</dt><dd>HTTP client: Microprofile client 1.x. JSON processing: JSON-B</dd><dt>**apache-httpclient**</dt><dd>HTTP client: Apache httpclient 4.x</dd></dl>|okhttp-gson|
|library|library template (sub-template) to use|<dl><dt>**jersey1**</dt><dd>HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.9.x. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey3' or other HTTP libraries instead.</dd><dt>**jersey2**</dt><dd>HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.9.x</dd><dt>**jersey3**</dt><dd>HTTP client: Jersey client 3.x. JSON processing: Jackson 2.x</dd><dt>**feign**</dt><dd>HTTP client: OpenFeign 10.x. JSON processing: Jackson 2.9.x. or Gson 2.x</dd><dt>**okhttp-gson**</dt><dd>[DEFAULT] HTTP client: OkHttp 3.x. JSON processing: Gson 2.8.x. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.</dd><dt>**retrofit2**</dt><dd>HTTP client: OkHttp 3.x. JSON processing: Gson 2.x (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)</dd><dt>**resttemplate**</dt><dd>HTTP client: Spring RestTemplate 4.x. JSON processing: Jackson 2.9.x</dd><dt>**webclient**</dt><dd>HTTP client: Spring WebClient 5.x. JSON processing: Jackson 2.9.x</dd><dt>**resteasy**</dt><dd>HTTP client: Resteasy client 3.x. JSON processing: Jackson 2.9.x</dd><dt>**vertx**</dt><dd>HTTP client: VertX client 3.x. JSON processing: Jackson 2.9.x</dd><dt>**google-api-client**</dt><dd>HTTP client: Google API client 1.x. JSON processing: Jackson 2.9.x</dd><dt>**rest-assured**</dt><dd>HTTP client: rest-assured : 4.x. JSON processing: Gson 2.x or Jackson 2.10.x. Only for Java 8</dd><dt>**native**</dt><dd>HTTP client: Java native HttpClient. JSON processing: Jackson 2.9.x. Only for Java11+</dd><dt>**microprofile**</dt><dd>HTTP client: Microprofile client 1.x. JSON processing: JSON-B or Jackson 2.9.x</dd><dt>**apache-httpclient**</dt><dd>HTTP client: Apache httpclient 4.x</dd></dl>|okhttp-gson|
|licenseName|The name of the license| |Unlicense|
|licenseUrl|The URL of the license| |http://unlicense.org|
|microprofileFramework|Framework for microprofile. Possible values &quot;kumuluzee&quot;| |null|
|microprofileMutiny|Whether to use async types for microprofile (currently only Smallrye Mutiny is supported).| |null|
|microprofileRestClientVersion|Version of MicroProfile Rest Client API.| |null|
|modelPackage|package for generated models| |org.openapitools.client.model|
|openApiNullable|Enable OpenAPI Jackson Nullable library| |true|
Expand Down
Expand Up @@ -65,6 +65,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public static final String USE_REFLECTION_EQUALS_HASHCODE = "useReflectionEqualsHashCode";
public static final String CASE_INSENSITIVE_RESPONSE_HEADERS = "caseInsensitiveResponseHeaders";
public static final String MICROPROFILE_FRAMEWORK = "microprofileFramework";
public static final String MICROPROFILE_MUTINY = "microprofileMutiny";
wing328 marked this conversation as resolved.
Show resolved Hide resolved
public static final String USE_ABSTRACTION_FOR_FILES = "useAbstractionForFiles";
public static final String DYNAMIC_OPERATIONS = "dynamicOperations";
public static final String SUPPORT_STREAMING = "supportStreaming";
Expand Down Expand Up @@ -106,6 +107,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
protected boolean doNotUseRx = true;
protected boolean usePlayWS = false;
protected String microprofileFramework = MICROPROFILE_DEFAULT;
protected boolean microprofileMutiny = false;
protected String configKey = null;

protected boolean asyncNative = false;
Expand Down Expand Up @@ -197,6 +199,7 @@ public JavaClientCodegen() {
cliOptions.add(CliOption.newBoolean(USE_REFLECTION_EQUALS_HASHCODE, "Use org.apache.commons.lang3.builder for equals and hashCode in the models. WARNING: This will fail under a security manager, unless the appropriate permissions are set up correctly and also there's potential performance impact."));
cliOptions.add(CliOption.newBoolean(CASE_INSENSITIVE_RESPONSE_HEADERS, "Make API response's headers case-insensitive. Available on " + OKHTTP_GSON + ", " + JERSEY2 + " libraries"));
cliOptions.add(CliOption.newString(MICROPROFILE_FRAMEWORK, "Framework for microprofile. Possible values \"kumuluzee\""));
cliOptions.add(CliOption.newString(MICROPROFILE_MUTINY, "Whether to use async types for microprofile (currently only Smallrye Mutiny is supported)."));
cliOptions.add(CliOption.newBoolean(USE_ABSTRACTION_FOR_FILES, "Use alternative types instead of java.io.File to allow passing bytes without a file on disk. Available on resttemplate, webclient, libraries"));
cliOptions.add(CliOption.newBoolean(DYNAMIC_OPERATIONS, "Generate operations dynamically at runtime from an OAS", this.dynamicOperations));
cliOptions.add(CliOption.newBoolean(SUPPORT_STREAMING, "Support streaming endpoint (beta)", this.supportStreaming));
Expand All @@ -221,7 +224,7 @@ public JavaClientCodegen() {
supportedLibraries.put(GOOGLE_API_CLIENT, "HTTP client: Google API client 1.x. JSON processing: Jackson 2.9.x");
supportedLibraries.put(REST_ASSURED, "HTTP client: rest-assured : 4.x. JSON processing: Gson 2.x or Jackson 2.10.x. Only for Java 8");
supportedLibraries.put(NATIVE, "HTTP client: Java native HttpClient. JSON processing: Jackson 2.9.x. Only for Java11+");
supportedLibraries.put(MICROPROFILE, "HTTP client: Microprofile client 1.x. JSON processing: JSON-B");
supportedLibraries.put(MICROPROFILE, "HTTP client: Microprofile client 1.x. JSON processing: JSON-B or Jackson 2.9.x");
supportedLibraries.put(APACHE, "HTTP client: Apache httpclient 4.x");

CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
Expand Down Expand Up @@ -333,6 +336,10 @@ public void processOpts() {
}
additionalProperties.put(MICROPROFILE_FRAMEWORK, microprofileFramework);

if (additionalProperties.containsKey(MICROPROFILE_MUTINY)) {
this.setMicroprofileMutiny(convertPropertyToBooleanAndWriteBack(MICROPROFILE_MUTINY));
}

if (!additionalProperties.containsKey(MICROPROFILE_REST_CLIENT_VERSION)) {
additionalProperties.put(MICROPROFILE_REST_CLIENT_VERSION, MICROPROFILE_REST_CLIENT_DEFAULT_VERSION);
} else {
Expand Down Expand Up @@ -625,7 +632,12 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("api_exception.mustache", apiExceptionFolder, "ApiException.java"));
supportingFiles.add(new SupportingFile("api_exception_mapper.mustache", apiExceptionFolder, "ApiExceptionMapper.java"));
serializationLibrary = "none";
if (getSerializationLibrary() == null) {
LOGGER.info("No serializationLibrary configured, using '{}' as fallback", SERIALIZATION_LIBRARY_JSONB);
setSerializationLibrary(SERIALIZATION_LIBRARY_JSONB);
} else if (getSerializationLibrary().equals(SERIALIZATION_LIBRARY_GSON)) {
forceSerializationLibrary(SERIALIZATION_LIBRARY_JSONB);
}

if (microprofileFramework.equals(MICROPROFILE_KUMULUZEE)) {
supportingFiles.add(new SupportingFile("kumuluzee.pom.mustache", "", "pom.xml"));
Expand Down Expand Up @@ -1102,7 +1114,11 @@ public void setMicroprofileFramework(String microprofileFramework) {
this.microprofileFramework = microprofileFramework;
}

public void setConfigKey(String configKey) {
public void setMicroprofileMutiny(boolean microprofileMutiny) {
this.microprofileMutiny = microprofileMutiny;
}

public void setConfigKey(String configKey) {
this.configKey = configKey;
}

Expand Down
Expand Up @@ -15,6 +15,9 @@ import {{rootJavaEEPackage}}.ws.rs.core.MediaType;
{{^disableMultipart}}
import org.apache.cxf.jaxrs.ext.multipart.*;
{{/disableMultipart}}
{{#microprofileMutiny}}
import io.smallrye.mutiny.Uni;
{{/microprofileMutiny}}

{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.*;
Expand Down Expand Up @@ -66,7 +69,7 @@ public interface {{classname}} {
{{#hasProduces}}
@Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} })
{{/hasProduces}}
public {{{returnType}}}{{^returnType}}void{{/returnType}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException, ProcessingException;
{{^vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni<{{{returnType}}}>{{/microprofileMutiny}}{{^microprofileMutiny}}{{{returnType}}}{{/microprofileMutiny}}{{/vendorExtensions.x-java-is-response-void}}{{#vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni<Void>{{/microprofileMutiny}}{{^microprofileMutiny}}void{{/microprofileMutiny}}{{/vendorExtensions.x-java-is-response-void}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException, ProcessingException;
{{/operation}}
}
{{/operations}}
Expand Up @@ -72,7 +72,7 @@ public class {{classname}}Test {
{{#allParams}}
{{^isFile}}{{{dataType}}} {{paramName}} = null;{{/isFile}}{{#isFile}}org.apache.cxf.jaxrs.ext.multipart.Attachment {{paramName}} = null;{{/isFile}}
{{/allParams}}
//{{#returnType}}{{{.}}} response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
//{{^vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni<{{{returnType}}}>{{/microprofileMutiny}}{{^microprofileMutiny}}{{{returnType}}}{{/microprofileMutiny}} response = {{/vendorExtensions.x-java-is-response-void}}api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
//{{#returnType}}assertNotNull(response);{{/returnType}}


Expand Down
Expand Up @@ -3,8 +3,10 @@
@XmlEnum({{dataType}}.class)
{{/withXml}}
{{^withXml}}
{{#jsonb}}
@JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class)
@JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class)
{{/jsonb}}
{{/withXml}}
{{>additionalEnumTypeAnnotations}}public enum {{datatypeWithEnum}} {

Expand All @@ -24,6 +26,9 @@
value = v;
}

{{#jackson}}
@JsonValue
{{/jackson}}
public {{dataType}} value() {
return value;
}
Expand All @@ -44,6 +49,7 @@
}
{{/withXml}}
{{^withXml}}
{{#jsonb}}
public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> {
@Override
public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
Expand All @@ -62,5 +68,17 @@
generator.write(obj.value);
}
}
{{/jsonb}}
{{#jackson}}
@JsonCreator
public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) {
for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
if (b.value.equals(value)) {
return b;
}
}
{{#isNullable}}return null;{{/isNullable}}{{^isNullable}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/isNullable}}
}
{{/jackson}}
{{/withXml}}
}
Expand Up @@ -156,6 +156,13 @@
<version>${jakarta.annotation.version}</version>
<scope>provided</scope>
</dependency>
{{#microprofileMutiny}}
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>mutiny</artifactId>
<version>${mutiny.version}</version>
</dependency>
{{/microprofileMutiny}}
</dependencies>
<repositories>
<repository>
Expand Down Expand Up @@ -196,5 +203,8 @@
<maven.failsafe.plugin.version>2.6</maven.failsafe.plugin.version>
<build.helper.maven.plugin.version>1.9.1</build.helper.maven.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
{{#microprofileMutiny}}
<mutiny.version>1.2.0</mutiny.version>
{{/microprofileMutiny}}
</properties>
</project>

This file was deleted.

Expand Up @@ -52,7 +52,7 @@ public interface PetApi {

@Consumes({ "application/json", "application/xml" })
@Produces({ "application/xml", "application/json" })
public Pet addPet(Pet pet) throws ApiException, ProcessingException;
Pet addPet(Pet pet) throws ApiException, ProcessingException;
wing328 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Deletes a pet
Expand All @@ -62,7 +62,7 @@ public interface PetApi {
*/
@DELETE
@Path("/{petId}")
public void deletePet(@PathParam("petId") Long petId, @HeaderParam("api_key") String apiKey) throws ApiException, ProcessingException;
void deletePet(@PathParam("petId") Long petId, @HeaderParam("api_key") String apiKey) throws ApiException, ProcessingException;

/**
* Finds Pets by status
Expand All @@ -73,7 +73,7 @@ public interface PetApi {
@GET
@Path("/findByStatus")
@Produces({ "application/xml", "application/json" })
public List<Pet> findPetsByStatus(@QueryParam("status") List<String> status) throws ApiException, ProcessingException;
List<Pet> findPetsByStatus(@QueryParam("status") List<String> status) throws ApiException, ProcessingException;

/**
* Finds Pets by tags
Expand All @@ -86,7 +86,7 @@ public interface PetApi {
@GET
@Path("/findByTags")
@Produces({ "application/xml", "application/json" })
public List<Pet> findPetsByTags(@QueryParam("tags") List<String> tags) throws ApiException, ProcessingException;
List<Pet> findPetsByTags(@QueryParam("tags") List<String> tags) throws ApiException, ProcessingException;

/**
* Find pet by ID
Expand All @@ -97,7 +97,7 @@ public interface PetApi {
@GET
@Path("/{petId}")
@Produces({ "application/xml", "application/json" })
public Pet getPetById(@PathParam("petId") Long petId) throws ApiException, ProcessingException;
Pet getPetById(@PathParam("petId") Long petId) throws ApiException, ProcessingException;

/**
* Update an existing pet
Expand All @@ -109,7 +109,7 @@ public interface PetApi {

@Consumes({ "application/json", "application/xml" })
@Produces({ "application/xml", "application/json" })
public Pet updatePet(Pet pet) throws ApiException, ProcessingException;
Pet updatePet(Pet pet) throws ApiException, ProcessingException;

/**
* Updates a pet in the store with form data
Expand All @@ -120,7 +120,7 @@ public interface PetApi {
@POST
@Path("/{petId}")
@Consumes({ "application/x-www-form-urlencoded" })
public void updatePetWithForm(@PathParam("petId") Long petId, @Multipart(value = "name", required = false) String name, @Multipart(value = "status", required = false) String status) throws ApiException, ProcessingException;
void updatePetWithForm(@PathParam("petId") Long petId, @Multipart(value = "name", required = false) String name, @Multipart(value = "status", required = false) String status) throws ApiException, ProcessingException;

/**
* uploads an image
Expand All @@ -132,5 +132,5 @@ public interface PetApi {
@Path("/{petId}/uploadImage")
@Consumes({ "multipart/form-data" })
@Produces({ "application/json" })
public ModelApiResponse uploadFile(@PathParam("petId") Long petId, @Multipart(value = "additionalMetadata", required = false) String additionalMetadata, @Multipart(value = "file" , required = false) Attachment _fileDetail) throws ApiException, ProcessingException;
ModelApiResponse uploadFile(@PathParam("petId") Long petId, @Multipart(value = "additionalMetadata", required = false) String additionalMetadata, @Multipart(value = "file" , required = false) Attachment _fileDetail) throws ApiException, ProcessingException;
}