diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md
index 11a92e692023..517f907c5fd9 100644
--- a/docs/generators/java-microprofile.md
+++ b/docs/generators/java-microprofile.md
@@ -62,7 +62,7 @@ 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).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
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.
|true|
-|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson|
+|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
**apache-asynchttpclient**
HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson|
|licenseName|The name of the license| |Unlicense|
|licenseUrl|The URL of the license| |http://unlicense.org|
|microprofileFramework|Framework for microprofile. Possible values "kumuluzee"| |null|
@@ -86,7 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|source folder for generated code| |src/main/java|
|supportStreaming|Support streaming endpoint (beta)| |false|
-|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.| |false|
+|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries.| |false|
|testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi|
|useAbstractionForFiles|Use alternative types instead of java.io.File to allow passing bytes without a file on disk. Available on resttemplate, webclient, restclient, libraries| |false|
|useBeanValidation|Use BeanValidation API annotations| |false|
diff --git a/docs/generators/java.md b/docs/generators/java.md
index 184a6d1ca5f8..f5c1d5f6b1d3 100644
--- a/docs/generators/java.md
+++ b/docs/generators/java.md
@@ -62,7 +62,7 @@ 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).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
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.
|true|
-|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson|
+|library|library template (sub-template) to use|
**jersey2**
HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.17.1
**jersey3**
HTTP client: Jersey client 3.1.1. JSON processing: Jackson 2.17.1
**feign**
HTTP client: OpenFeign 13.2.1. JSON processing: Jackson 2.17.1 or Gson 2.10.1
**okhttp-gson**
[DEFAULT] HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
**retrofit2**
HTTP client: OkHttp 4.11.0. JSON processing: Gson 2.10.1 (Retrofit 2.5.0) or Jackson 2.17.1. Enable the RxJava adapter using '-DuseRxJava[2/3]=true'. (RxJava 1.x or 2.x or 3.x)
**resttemplate**
HTTP client: Spring RestTemplate 5.3.33 (6.1.5 if `useJakartaEe=true`). JSON processing: Jackson 2.17.1
**webclient**
HTTP client: Spring WebClient 5.1.18. JSON processing: Jackson 2.17.1
**restclient**
HTTP client: Spring RestClient 6.1.6. JSON processing: Jackson 2.17.1
**resteasy**
HTTP client: Resteasy client 4.7.6. JSON processing: Jackson 2.17.1
**vertx**
HTTP client: VertX client 3.5.2. JSON processing: Jackson 2.17.1
**google-api-client**
HTTP client: Google API client 2.2.0. JSON processing: Jackson 2.17.1
**rest-assured**
HTTP client: rest-assured 5.3.2. JSON processing: Gson 2.10.1 or Jackson 2.17.1. Only for Java 8
**native**
HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+
**microprofile**
HTTP client: Microprofile client 2.0 (default, set desired version via `microprofileRestClientVersion=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1
**apache-httpclient**
HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1
**apache-asynchttpclient**
HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1
|okhttp-gson|
|licenseName|The name of the license| |Unlicense|
|licenseUrl|The URL of the license| |http://unlicense.org|
|microprofileFramework|Framework for microprofile. Possible values "kumuluzee"| |null|
@@ -86,7 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|source folder for generated code| |src/main/java|
|supportStreaming|Support streaming endpoint (beta)| |false|
-|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.| |false|
+|supportUrlQuery|Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries.| |false|
|testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi|
|useAbstractionForFiles|Use alternative types instead of java.io.File to allow passing bytes without a file on disk. Available on resttemplate, webclient, restclient, libraries| |false|
|useBeanValidation|Use BeanValidation API annotations| |false|
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java
index 45b62581f15f..3c05be45e526 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java
@@ -93,6 +93,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public static final String VERTX = "vertx";
public static final String MICROPROFILE = "microprofile";
public static final String APACHE = "apache-httpclient";
+ public static final String APACHE_ASYNC = "apache-asynchttpclient";
public static final String MICROPROFILE_REST_CLIENT_VERSION = "microprofileRestClientVersion";
public static final String MICROPROFILE_REST_CLIENT_DEFAULT_VERSION = "2.0";
public static final String MICROPROFILE_REST_CLIENT_DEFAULT_ROOT_PACKAGE = "javax";
@@ -240,7 +241,7 @@ public JavaClientCodegen() {
cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient support this option."));
cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations));
cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean));
- cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries."));
+ cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` and `apache-asynchttpclient libraries."));
cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive));
cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties));
@@ -259,6 +260,7 @@ public JavaClientCodegen() {
supportedLibraries.put(NATIVE, "HTTP client: Java native HttpClient. JSON processing: Jackson 2.17.1. Only for Java11+");
supportedLibraries.put(MICROPROFILE, "HTTP client: Microprofile client " + MICROPROFILE_REST_CLIENT_DEFAULT_VERSION + " (default, set desired version via `" + MICROPROFILE_REST_CLIENT_VERSION + "=x.x.x`). JSON processing: JSON-B 1.0.2 or Jackson 2.17.1");
supportedLibraries.put(APACHE, "HTTP client: Apache httpclient 5.2.1. JSON processing: Jackson 2.17.1");
+ supportedLibraries.put(APACHE_ASYNC, "HTTP client: Apache async httpclient 5.2.1. JSON processing: Jackson 2.17.1");
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
@@ -395,9 +397,9 @@ public void processOpts() {
convertPropertyToBooleanAndWriteBack(WEBCLIENT_BLOCKING_OPERATIONS, op -> webclientBlockingOperations=op);
convertPropertyToBooleanAndWriteBack(FAIL_ON_UNKNOWN_PROPERTIES, this::setFailOnUnknownProperties);
- // add URL query deepObject support to native, apache-httpclient by default
+ // add URL query deepObject support to native, apache-httpclient, apache-asynchttpclient by default
if (!additionalProperties.containsKey(SUPPORT_URL_QUERY)) {
- if (isLibrary(NATIVE) || isLibrary(APACHE)) {
+ if (isLibrary(NATIVE) || isLibrary(APACHE) || isLibrary(APACHE_ASYNC)) {
// default to true for native and apache-httpclient
additionalProperties.put(SUPPORT_URL_QUERY, true);
}
@@ -436,7 +438,7 @@ public void processOpts() {
}
// helper for client library that allow to parse/format java.time.OffsetDateTime or org.threeten.bp.OffsetDateTime
- if (additionalProperties.containsKey("jsr310") && (isLibrary(WEBCLIENT) || isLibrary(VERTX) || isLibrary(RESTTEMPLATE) || isLibrary(RESTEASY) || isLibrary(MICROPROFILE) || isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(APACHE) || isLibrary(RESTCLIENT))) {
+ if (additionalProperties.containsKey("jsr310") && (isLibrary(WEBCLIENT) || isLibrary(VERTX) || isLibrary(RESTTEMPLATE) || isLibrary(RESTEASY) || isLibrary(MICROPROFILE) || isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(APACHE) || isLibrary(APACHE_ASYNC) || isLibrary(RESTCLIENT))) {
supportingFiles.add(new SupportingFile("JavaTimeFormatter.mustache", invokerFolder, "JavaTimeFormatter.java"));
}
@@ -492,7 +494,7 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java"));
}
- if (APACHE.equals(getLibrary()) || RESTTEMPLATE.equals(getLibrary())) {
+ if (APACHE.equals(getLibrary()) || APACHE_ASYNC.equals(getLibrary()) || RESTTEMPLATE.equals(getLibrary())) {
supportingFiles.add(new SupportingFile("BaseApi.mustache", invokerFolder, "BaseApi.java"));
}
@@ -644,7 +646,7 @@ public void processOpts() {
additionalProperties.put("jsonbPolymorphism", true);
}
}
- } else if (APACHE.equals(getLibrary())) {
+ } else if (APACHE.equals(getLibrary()) || (APACHE_ASYNC.equals(getLibrary()))) {
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);
} else {
LOGGER.error("Unknown library option (-l/--library): {}", getLibrary());
@@ -814,7 +816,7 @@ public int compare(CodegenParameter one, CodegenParameter another) {
}
}
- if (NATIVE.equals(getLibrary()) || APACHE.equals(getLibrary())) {
+ if (NATIVE.equals(getLibrary()) || APACHE.equals(getLibrary()) || APACHE_ASYNC.equals(getLibrary())) {
OperationMap operations = objs.getOperations();
List operationList = operations.getOperation();
Pattern methodPattern = Pattern.compile("^(.*):([^:]*)$");
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache
new file mode 100644
index 000000000000..eed11a7b9490
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/ApiClient.mustache
@@ -0,0 +1,1135 @@
+{{>licenseInfo}}
+package {{invokerPackage}};
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+{{#joda}}
+import com.fasterxml.jackson.datatype.joda.JodaModule;
+{{/joda}}
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import java.time.OffsetDateTime;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+{{#openApiNullable}}
+import org.openapitools.jackson.nullable.JsonNullableModule;
+{{/openApiNullable}}
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+
+import org.apache.hc.client5.http.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.Cookie;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.FileEntity;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.function.Supplier;
+import java.util.TimeZone;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import java.net.URLEncoder;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Paths;
+import java.lang.reflect.Type;
+import java.net.URI;
+
+import java.text.DateFormat;
+
+import {{invokerPackage}}.auth.Authentication;
+{{#hasHttpBasicMethods}}
+import {{invokerPackage}}.auth.HttpBasicAuth;
+{{/hasHttpBasicMethods}}
+{{#hasHttpBearerMethods}}
+import {{invokerPackage}}.auth.HttpBearerAuth;
+{{/hasHttpBearerMethods}}
+{{#hasApiKeyMethods}}
+import {{invokerPackage}}.auth.ApiKeyAuth;
+{{/hasApiKeyMethods}}
+{{#hasOAuthMethods}}
+import {{invokerPackage}}.auth.OAuth;
+{{/hasOAuthMethods}}
+
+{{>generatedAnnotation}}
+public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
+ private Map defaultHeaderMap = new HashMap();
+ private Map defaultCookieMap = new HashMap();
+ private String basePath = "{{{basePath}}}";
+ protected List servers = new ArrayList({{#servers}}{{#-first}}Arrays.asList(
+{{/-first}} new ServerConfiguration(
+ "{{{url}}}",
+ "{{{description}}}{{^description}}No description provided{{/description}}",
+ new HashMap(){{#variables}}{{#-first}} {{
+{{/-first}} put("{{{name}}}", new ServerVariable(
+ "{{{description}}}{{^description}}No description provided{{/description}}",
+ "{{{defaultValue}}}",
+ new HashSet(
+ {{#enumValues}}
+ {{#-first}}
+ Arrays.asList(
+ {{/-first}}
+ "{{{.}}}"{{^-last}},{{/-last}}
+ {{#-last}}
+ )
+ {{/-last}}
+ {{/enumValues}}
+ )
+ ));
+ {{#-last}}
+ }}{{/-last}}{{/variables}}
+ ){{^-last}},{{/-last}}
+ {{#-last}}
+ ){{/-last}}{{/servers}});
+ protected Integer serverIndex = 0;
+ protected Map serverVariables = null;
+ private boolean debugging = false;
+ private int connectionTimeout = 0;
+
+ private CloseableHttpAsyncClient httpClient;
+ private ObjectMapper objectMapper;
+ protected String tempFolderPath = null;
+
+ private Map authentications;
+
+ private Map lastStatusCodeByThread = new ConcurrentHashMap<>();
+ private Map>> lastResponseHeadersByThread = new ConcurrentHashMap<>();
+
+ private DateFormat dateFormat;
+
+ // Methods that can have a request body
+ private static List bodyMethods = Arrays.asList("POST", "PUT", "DELETE", "PATCH");
+
+ public ApiClient(CloseableHttpAsyncClient httpClient) {
+ objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}});
+ objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
+ objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+ objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+ objectMapper.registerModule(new Jdk8Module());
+ {{#joda}}
+ objectMapper.registerModule(new JodaModule());
+ {{/joda}}
+ objectMapper.registerModule(new JavaTimeModule());
+ {{#openApiNullable}}
+ objectMapper.registerModule(new JsonNullableModule());
+ {{/openApiNullable}}
+ objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat());
+
+ dateFormat = ApiClient.buildDefaultDateFormat();
+
+ // Set default User-Agent.
+ setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}");
+
+ // Setup authentications (key: authentication name, value: authentication).
+ authentications = new HashMap();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
+ authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{#isBasicBearer}}
+ authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}
+ authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
+ authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
+ // Prevent the authentications from being modified.
+ authentications = Collections.unmodifiableMap(authentications);
+
+ this.httpClient = httpClient;
+ }
+
+ public ApiClient() {
+ this(HttpAsyncClients.createDefault());
+ }
+
+ public static DateFormat buildDefaultDateFormat() {
+ return new RFC3339DateFormat();
+ }
+
+ /**
+ * Returns the current object mapper used for JSON serialization/deserialization.
+ *
+ * Note: If you make changes to the object mapper, remember to set it back via
+ * setObjectMapper in order to trigger HTTP client rebuilding.
+ *
+ * @return Object mapper
+ */
+ public ObjectMapper getObjectMapper() {
+ return objectMapper;
+ }
+
+ /**
+ * Sets the object mapper.
+ *
+ * @param objectMapper object mapper
+ * @return API client
+ */
+ public ApiClient setObjectMapper(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ return this;
+ }
+
+ public CloseableHttpAsyncClient getHttpClient() {
+ return httpClient;
+ }
+
+ /**
+ * Sets the HTTP client.
+ *
+ * @param httpClient HTTP client
+ * @return API client
+ */
+ public ApiClient setHttpClient(CloseableHttpAsyncClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ public String getBasePath() {
+ return basePath;
+ }
+
+ /**
+ * Sets the base path.
+ *
+ * @param basePath base path
+ * @return API client
+ */
+ public ApiClient setBasePath(String basePath) {
+ this.basePath = basePath;
+ this.serverIndex = null;
+ return this;
+ }
+
+ public List getServers() {
+ return servers;
+ }
+
+ /**
+ * Sets the server.
+ *
+ * @param servers a list of server configuration
+ * @return API client
+ */
+ public ApiClient setServers(List servers) {
+ this.servers = servers;
+ return this;
+ }
+
+ public Integer getServerIndex() {
+ return serverIndex;
+ }
+
+ /**
+ * Sets the server index.
+ *
+ * @param serverIndex server index
+ * @return API client
+ */
+ public ApiClient setServerIndex(Integer serverIndex) {
+ this.serverIndex = serverIndex;
+ return this;
+ }
+
+ public Map getServerVariables() {
+ return serverVariables;
+ }
+
+ /**
+ * Sets the server variables.
+ *
+ * @param serverVariables server variables
+ * @return API client
+ */
+ public ApiClient setServerVariables(Map serverVariables) {
+ this.serverVariables = serverVariables;
+ return this;
+ }
+
+ /**
+ * Gets the status code of the previous request
+ *
+ * @return Status code
+ */
+ @Deprecated
+ public int getStatusCode() {
+ return lastStatusCodeByThread.get(Thread.currentThread().getId());
+ }
+
+ /**
+ * Gets the response headers of the previous request
+ * @return Response headers
+ */
+ @Deprecated
+ public Map> getResponseHeaders() {
+ return lastResponseHeadersByThread.get(Thread.currentThread().getId());
+ }
+
+ /**
+ * Get authentications (key: authentication name, value: authentication).
+ * @return Map of authentication
+ */
+ public Map getAuthentications() {
+ return authentications;
+ }
+
+ /**
+ * Get authentication for the given name.
+ *
+ * @param authName The authentication name
+ * @return The authentication, null if not found
+ */
+ public Authentication getAuthentication(String authName) {
+ return authentications.get(authName);
+ }
+
+ /**
+ * The path of temporary folder used to store downloaded files from endpoints
+ * with file response. The default value is null, i.e. using
+ * the system's default temporary folder.
+ *
+ * @return Temp folder path
+ */
+ public String getTempFolderPath() {
+ return tempFolderPath;
+ }
+
+ {{#hasHttpBearerMethods}}
+ /**
+ * Helper method to set access token for the first Bearer authentication.
+ * @param bearerToken Bearer token
+ * @return API client
+ */
+ public ApiClient setBearerToken(String bearerToken) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof HttpBearerAuth) {
+ ((HttpBearerAuth) auth).setBearerToken(bearerToken);
+ return this;
+ }
+ }
+ throw new RuntimeException("No Bearer authentication configured!");
+ }
+
+ /**
+ * Helper method to set the supplier of access tokens for Bearer authentication.
+ *
+ * @param tokenSupplier the token supplier function
+ */
+ public void setBearerToken(Supplier tokenSupplier) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof HttpBearerAuth) {
+ ((HttpBearerAuth) auth).setBearerToken(tokenSupplier);
+ return;
+ }
+ }
+ throw new RuntimeException("No Bearer authentication configured!");
+ }
+ {{/hasHttpBearerMethods}}
+
+ {{#hasHttpBasicMethods}}
+ /**
+ * Helper method to set username for the first HTTP basic authentication.
+ * @param username Username
+ * @return API client
+ */
+ public ApiClient setUsername(String username) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof HttpBasicAuth) {
+ ((HttpBasicAuth) auth).setUsername(username);
+ return this;
+ }
+ }
+ throw new RuntimeException("No HTTP basic authentication configured!");
+ }
+
+ /**
+ * Helper method to set password for the first HTTP basic authentication.
+ * @param password Password
+ * @return API client
+ */
+ public ApiClient setPassword(String password) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof HttpBasicAuth) {
+ ((HttpBasicAuth) auth).setPassword(password);
+ return this;
+ }
+ }
+ throw new RuntimeException("No HTTP basic authentication configured!");
+ }
+
+ {{/hasHttpBasicMethods}}
+
+ {{#hasApiKeyMethods}}
+ /**
+ * Helper method to set API key value for the first API key authentication.
+ * @param apiKey the API key
+ * @return API client
+ */
+ public ApiClient setApiKey(String apiKey) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof ApiKeyAuth) {
+ ((ApiKeyAuth) auth).setApiKey(apiKey);
+ return this;
+ }
+ }
+ throw new RuntimeException("No API key authentication configured!");
+ }
+
+ /**
+ * Helper method to set API key prefix for the first API key authentication.
+ * @param apiKeyPrefix API key prefix
+ * @return API client
+ */
+ public ApiClient setApiKeyPrefix(String apiKeyPrefix) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof ApiKeyAuth) {
+ ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
+ return this;
+ }
+ }
+ throw new RuntimeException("No API key authentication configured!");
+ }
+
+ {{/hasApiKeyMethods}}
+
+ {{#hasOAuthMethods}}
+ /**
+ * Helper method to set access token for the first OAuth2 authentication.
+ * @param accessToken Access token
+ * @return API client
+ */
+ public ApiClient setAccessToken(String accessToken) {
+ for (Authentication auth : authentications.values()) {
+ if (auth instanceof OAuth) {
+ ((OAuth) auth).setAccessToken(accessToken);
+ return this;
+ }
+ }
+ throw new RuntimeException("No OAuth2 authentication configured!");
+ }
+
+ {{/hasOAuthMethods}}
+
+ /**
+ * Set the User-Agent header's value (by adding to the default header map).
+ * @param userAgent User agent
+ * @return API client
+ */
+ public ApiClient setUserAgent(String userAgent) {
+ addDefaultHeader("User-Agent", userAgent);
+ return this;
+ }
+
+ /**
+ * Set temp folder path
+ * @param tempFolderPath Temp folder path
+ * @return API client
+ */
+ public ApiClient setTempFolderPath(String tempFolderPath) {
+ this.tempFolderPath = tempFolderPath;
+ return this;
+ }
+
+ /**
+ * Add a default header.
+ *
+ * @param key The header's key
+ * @param value The header's value
+ * @return API client
+ */
+ public ApiClient addDefaultHeader(String key, String value) {
+ defaultHeaderMap.put(key, value);
+ return this;
+ }
+
+ /**
+ * Add a default cookie.
+ *
+ * @param key The cookie's key
+ * @param value The cookie's value
+ * @return API client
+ */
+ public ApiClient addDefaultCookie(String key, String value) {
+ defaultCookieMap.put(key, value);
+ return this;
+ }
+
+ /**
+ * Check that whether debugging is enabled for this API client.
+ * @return True if debugging is on
+ */
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+ /**
+ * Enable/disable debugging for this API client.
+ *
+ * @param debugging To enable (true) or disable (false) debugging
+ * @return API client
+ */
+ public ApiClient setDebugging(boolean debugging) {
+ // TODO: implement debugging mode
+ this.debugging = debugging;
+ return this;
+ }
+
+ /**
+ * Connect timeout (in milliseconds).
+ * @return Connection timeout
+ */
+ public int getConnectTimeout() {
+ return connectionTimeout;
+ }
+
+ /**
+ * Set the connect timeout (in milliseconds).
+ * A value of 0 means no timeout, otherwise values must be between 1 and
+ * {@link Integer#MAX_VALUE}.
+ * @param connectionTimeout Connection timeout in milliseconds
+ * @return API client
+ */
+ public ApiClient setConnectTimeout(int connectionTimeout) {
+ this.connectionTimeout = connectionTimeout;
+ return this;
+ }
+
+ /**
+ * Get the date format used to parse/format date parameters.
+ * @return Date format
+ */
+ public DateFormat getDateFormat() {
+ return dateFormat;
+ }
+
+ /**
+ * Set the date format used to parse/format date parameters.
+ * @param dateFormat Date format
+ * @return API client
+ */
+ public ApiClient setDateFormat(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ // Also set the date format for model (de)serialization with Date properties.
+ this.objectMapper.setDateFormat((DateFormat) dateFormat.clone());
+ return this;
+ }
+
+ /**
+ * Parse the given string into Date object.
+ * @param str String
+ * @return Date
+ */
+ public Date parseDate(String str) {
+ try {
+ return dateFormat.parse(str);
+ } catch (java.text.ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Format the given Date object into string.
+ * @param date Date
+ * @return Date in string format
+ */
+ public String formatDate(Date date) {
+ return dateFormat.format(date);
+ }
+
+ /**
+ * Format the given parameter object into string.
+ * @param param Object
+ * @return Object in string format
+ */
+ public String parameterToString(Object param) {
+ if (param == null) {
+ return "";
+ } else if (param instanceof Date) {
+ return formatDate((Date) param);
+ } {{#jsr310}}else if (param instanceof OffsetDateTime) {
+ return formatOffsetDateTime((OffsetDateTime) param);
+ } {{/jsr310}}else if (param instanceof Collection) {
+ StringBuilder b = new StringBuilder();
+ for(Object o : (Collection>)param) {
+ if(b.length() > 0) {
+ b.append(',');
+ }
+ b.append(String.valueOf(o));
+ }
+ return b.toString();
+ } else {
+ return String.valueOf(param);
+ }
+ }
+
+ /**
+ * Formats the specified query parameter to a list containing a single {@code Pair} object.
+ *
+ * Note that {@code value} must not be a collection.
+ *
+ * @param name The name of the parameter.
+ * @param value The value of the parameter.
+ * @return A list containing a single {@code Pair} object.
+ */
+ public List parameterToPair(String name, Object value) {
+ List params = new ArrayList();
+
+ // preconditions
+ if (name == null || name.isEmpty() || value == null || value instanceof Collection) {
+ return params;
+ }
+
+ params.add(new Pair(name, escapeString(parameterToString(value))));
+ return params;
+ }
+
+ /**
+ * Formats the specified collection query parameters to a list of {@code Pair} objects.
+ *
+ * Note that the values of each of the returned Pair objects are percent-encoded.
+ *
+ * @param collectionFormat The collection format of the parameter.
+ * @param name The name of the parameter.
+ * @param value The value of the parameter.
+ * @return A list of {@code Pair} objects.
+ */
+ public List parameterToPairs(String collectionFormat, String name, Collection value) {
+ List params = new ArrayList();
+
+ // preconditions
+ if (name == null || name.isEmpty() || value == null || value.isEmpty()) {
+ return params;
+ }
+
+ // create the params based on the collection format
+ if ("multi".equals(collectionFormat)) {
+ for (Object item : value) {
+ params.add(new Pair(name, escapeString(parameterToString(item))));
+ }
+ return params;
+ }
+
+ // collectionFormat is assumed to be "csv" by default
+ String delimiter = ",";
+
+ // escape all delimiters except commas, which are URI reserved
+ // characters
+ if ("ssv".equals(collectionFormat)) {
+ delimiter = escapeString(" ");
+ } else if ("tsv".equals(collectionFormat)) {
+ delimiter = escapeString("\t");
+ } else if ("pipes".equals(collectionFormat)) {
+ delimiter = escapeString("|");
+ }
+
+ StringBuilder sb = new StringBuilder() ;
+ for (Object item : value) {
+ sb.append(delimiter);
+ sb.append(escapeString(parameterToString(item)));
+ }
+
+ params.add(new Pair(name, sb.substring(delimiter.length())));
+
+ return params;
+ }
+
+ /**
+ * Check if the given MIME is a JSON MIME.
+ * JSON MIME examples:
+ * application/json
+ * application/json; charset=UTF8
+ * APPLICATION/JSON
+ * application/vnd.company+json
+ * @param mime MIME
+ * @return True if MIME type is boolean
+ */
+ public boolean isJsonMime(String mime) {
+ String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
+ return mime != null && (mime.matches(jsonMime) || mime.equals("*/*"));
+ }
+
+ /**
+ * Select the Accept header's value from the given accepts array:
+ * if JSON exists in the given array, use it;
+ * otherwise use all of them (joining into a string)
+ *
+ * @param accepts The accepts array to select from
+ * @return The Accept header to use. If the given array is empty,
+ * null will be returned (not to set the Accept header explicitly).
+ */
+ public String selectHeaderAccept(String[] accepts) {
+ if (accepts.length == 0) {
+ return null;
+ }
+ for (String accept : accepts) {
+ if (isJsonMime(accept)) {
+ return accept;
+ }
+ }
+ return StringUtil.join(accepts, ",");
+ }
+
+ /**
+ * Select the Content-Type header's value from the given array:
+ * if JSON exists in the given array, use it;
+ * otherwise use the first one of the array.
+ *
+ * @param contentTypes The Content-Type array to select from
+ * @return The Content-Type header to use. If the given array is empty,
+ * or matches "any", JSON will be used.
+ */
+ public String selectHeaderContentType(String[] contentTypes) {
+ if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) {
+ return "application/json";
+ }
+ for (String contentType : contentTypes) {
+ if (isJsonMime(contentType)) {
+ return contentType;
+ }
+ }
+ return contentTypes[0];
+ }
+
+ /**
+ * Escape the given string to be used as URL query value.
+ * @param str String
+ * @return Escaped string
+ */
+ public String escapeString(String str) {
+ try {
+ return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20");
+ } catch (UnsupportedEncodingException e) {
+ return str;
+ }
+ }
+
+ /**
+ * Transforms response headers into map.
+ *
+ * @param headers HTTP headers
+ * @return a map of string array
+ */
+ protected Map> transformResponseHeaders(Header[] headers) {
+ Map> headersMap = new HashMap<>();
+ for (Header header : headers) {
+ List valuesList = headersMap.get(header.getName());
+ if (valuesList != null) {
+ valuesList.add(header.getValue());
+ } else {
+ valuesList = new ArrayList<>();
+ valuesList.add(header.getValue());
+ headersMap.put(header.getName(), valuesList);
+ }
+ }
+ return headersMap;
+ }
+
+ /**
+ * Parse content type object from header value
+ */
+ private ContentType getContentType(String headerValue) throws ApiException {
+ try {
+ return ContentType.parse(headerValue);
+ } catch (UnsupportedCharsetException e) {
+ throw new ApiException("Could not parse content type " + headerValue);
+ }
+ }
+
+ /**
+ * Get content type of a response or null if one was not provided
+ */
+ private String getResponseMimeType(HttpResponse response) throws ApiException {
+ Header contentTypeHeader = response.getFirstHeader("Content-Type");
+ if (contentTypeHeader != null) {
+ return getContentType(contentTypeHeader.getValue()).getMimeType();
+ }
+ return null;
+ }
+
+ /**
+ * Serialize the given Java object into string according the given
+ * Content-Type (only JSON is supported for now).
+ * @param obj Object
+ * @param contentType Content type
+ * @param formParams Form parameters
+ * @return Object
+ * @throws ApiException API exception
+ */
+ public String serialize(Object obj, Map formParams, ContentType contentType) throws ApiException {
+ String mimeType = contentType.getMimeType();
+ if (isJsonMime(mimeType)) {
+ try {
+ return objectMapper.writeValueAsString(obj);
+ } catch (JsonProcessingException e) {
+ throw new ApiException(e);
+ }
+ } else {
+ throw new ApiException("Serialization for content type '" + contentType + "' not supported");
+ }
+ }
+
+ /**
+ * Deserialize response body to Java object according to the Content-Type.
+ *
+ * @param Type
+ * @param response Response
+ * @param valueType Return type
+ * @return Deserialized object
+ * @throws ApiException API exception
+ * @throws IOException IO exception
+ */
+ @SuppressWarnings("unchecked")
+ public T deserialize(SimpleHttpResponse response, TypeReference valueType) throws ApiException, IOException, ParseException {
+ if (valueType == null) {
+ return null;
+ }
+ Type valueRawType = valueType.getType();
+ if (valueRawType.equals(byte[].class)) {
+ return (T) response.getBodyBytes();
+ } else if (valueRawType.equals(File.class)) {
+ return (T) downloadFileFromResponse(response);
+ }
+ String mimeType = getResponseMimeType(response);
+ if (mimeType == null || isJsonMime(mimeType)) {
+ // Assume json if no mime type
+ // convert input stream to string
+ String content = response.getBodyText();
+
+ if ("".equals(content)) { // returns null for empty body
+ return null;
+ }
+
+ return objectMapper.readValue(content, valueType);
+ } else if (mimeType.toLowerCase().startsWith("text/")) {
+ // convert input stream to string
+ return (T) response.getBodyText();
+ } else {
+ Map> responseHeaders = transformResponseHeaders(response.getHeaders());
+ throw new ApiException(
+ "Deserialization for content type '" + mimeType + "' not supported for type '" + valueType + "'",
+ response.getCode(),
+ responseHeaders,
+ response.getBodyText()
+ );
+ }
+ }
+
+ private File downloadFileFromResponse(SimpleHttpResponse response) throws IOException {
+ Header contentDispositionHeader = response.getFirstHeader("Content-Disposition");
+ String contentDisposition = contentDispositionHeader == null ? null : contentDispositionHeader.getValue();
+ File file = prepareDownloadFile(contentDisposition);
+ InputStream is = new ByteArrayInputStream(response.getBodyBytes());
+ Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ return file;
+ }
+
+ protected File prepareDownloadFile(String contentDisposition) throws IOException {
+ String filename = null;
+ if (contentDisposition != null && !"".equals(contentDisposition)) {
+ // Get filename from the Content-Disposition header.
+ Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
+ Matcher matcher = pattern.matcher(contentDisposition);
+ if (matcher.find())
+ filename = matcher.group(1);
+ }
+
+ String prefix;
+ String suffix = null;
+ if (filename == null) {
+ prefix = "download-";
+ suffix = "";
+ } else {
+ int pos = filename.lastIndexOf('.');
+ if (pos == -1) {
+ prefix = filename + "-";
+ } else {
+ prefix = filename.substring(0, pos) + "-";
+ suffix = filename.substring(pos);
+ }
+ // Files.createTempFile requires the prefix to be at least three characters long
+ if (prefix.length() < 3)
+ prefix = "download-";
+ }
+
+ if (tempFolderPath == null)
+ return Files.createTempFile(prefix, suffix).toFile();
+ else
+ return Files.createTempFile(Paths.get(tempFolderPath), prefix, suffix).toFile();
+ }
+
+ /**
+ * Returns the URL of the client as defined by the server (if exists) or the base path.
+ *
+ * @return The URL for the client.
+ */
+ public String getBaseURL() {
+ String baseURL;
+ if (serverIndex != null) {
+ if (serverIndex < 0 || serverIndex >= servers.size()) {
+ throw new ArrayIndexOutOfBoundsException(String.format(
+ "Invalid index %d when selecting the host settings. Must be less than %d", serverIndex, servers.size()
+ ));
+ }
+ baseURL = servers.get(serverIndex).URL(serverVariables);
+ } else {
+ baseURL = basePath;
+ }
+ return baseURL;
+ }
+
+ /**
+ * Build full URL by concatenating base URL, the given sub path and query parameters.
+ *
+ * @param path The sub path
+ * @param queryParams The query parameters
+ * @param collectionQueryParams The collection query parameters
+ * @param urlQueryDeepObject URL query string of the deep object parameters
+ * @return The full URL
+ */
+ private String buildUrl(String path, List queryParams, List collectionQueryParams, String urlQueryDeepObject) {
+ String baseURL = getBaseURL();
+
+ final StringBuilder url = new StringBuilder();
+ url.append(baseURL).append(path);
+
+ if (queryParams != null && !queryParams.isEmpty()) {
+ // support (constant) query string in `path`, e.g. "/posts?draft=1"
+ String prefix = path.contains("?") ? "&" : "?";
+ for (Pair param : queryParams) {
+ if (param.getValue() != null) {
+ if (prefix != null) {
+ url.append(prefix);
+ prefix = null;
+ } else {
+ url.append("&");
+ }
+ String value = parameterToString(param.getValue());
+ // query parameter value already escaped as part of parameterToPair
+ url.append(escapeString(param.getName())).append("=").append(value);
+ }
+ }
+ }
+
+ if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) {
+ String prefix = url.toString().contains("?") ? "&" : "?";
+ for (Pair param : collectionQueryParams) {
+ if (param.getValue() != null) {
+ if (prefix != null) {
+ url.append(prefix);
+ prefix = null;
+ } else {
+ url.append("&");
+ }
+ String value = parameterToString(param.getValue());
+ // collection query parameter value already escaped as part of parameterToPairs
+ url.append(escapeString(param.getName())).append("=").append(value);
+ }
+ }
+ }
+
+ if (urlQueryDeepObject != null && urlQueryDeepObject.length() > 0) {
+ url.append(url.toString().contains("?") ? "&" : "?");
+ url.append(urlQueryDeepObject);
+ }
+
+ return url.toString();
+ }
+
+ protected boolean isSuccessfulStatus(int statusCode) {
+ return statusCode >= 200 && statusCode < 300;
+ }
+
+ protected boolean isBodyAllowed(String method) {
+ return bodyMethods.contains(method);
+ }
+
+ protected Cookie buildCookie(String key, String value, URI uri) {
+ BasicClientCookie cookie = new BasicClientCookie(key, value);
+ cookie.setDomain(uri.getHost());
+ cookie.setPath("/");
+ return cookie;
+ }
+
+ protected T processResponse(SimpleHttpResponse response, TypeReference returnType) throws ApiException, IOException, ParseException {
+ int statusCode = response.getCode();
+ lastStatusCodeByThread.put(Thread.currentThread().getId(), statusCode);
+ if (statusCode == HttpStatus.SC_NO_CONTENT) {
+ return null;
+ }
+
+ Map> responseHeaders = transformResponseHeaders(response.getHeaders());
+ lastResponseHeadersByThread.put(Thread.currentThread().getId(), responseHeaders);
+
+ if (isSuccessfulStatus(statusCode)) {
+ return this.deserialize(response, returnType);
+ } else {
+ String message = response.getBody().getBodyText();
+ throw new ApiException(message, statusCode, responseHeaders, message);
+ }
+ }
+
+ /**
+ * Invoke API by sending HTTP request with the given options.
+ *
+ * @param Type
+ * @param path The sub-path of the HTTP URL
+ * @param method The request method, one of "GET", "POST", "PUT", and "DELETE"
+ * @param queryParams The query parameters
+ * @param collectionQueryParams The collection query parameters
+ * @param urlQueryDeepObject A URL query string for deep object parameters
+ * @param body The request body object - if it is not binary, otherwise null
+ * @param headerParams The header parameters
+ * @param cookieParams The cookie parameters
+ * @param formParams The form parameters
+ * @param accept The request's Accept header
+ * @param contentType The request's Content-Type header
+ * @param authNames The authentications to apply
+ * @param returnType Return type
+ * @return The response body in type of string
+ * @throws ApiException API exception
+ */
+ public CompletableFuture invokeAPI(
+ String path,
+ String method,
+ List queryParams,
+ List collectionQueryParams,
+ String urlQueryDeepObject,
+ Object body,
+ Map headerParams,
+ Map cookieParams,
+ Map formParams,
+ String accept,
+ String contentType,
+ String[] authNames,
+ TypeReference returnType
+ ) throws ApiException {
+ if (body != null && !formParams.isEmpty()) {
+ throw new ApiException("Cannot have body and form params");
+ }
+
+ updateParamsForAuth(authNames, queryParams, headerParams, cookieParams);
+ final String url = buildUrl(path, queryParams, collectionQueryParams, urlQueryDeepObject);
+
+ SimpleRequestBuilder builder = SimpleRequestBuilder.create(method);
+ builder.setUri(url);
+
+ if (accept != null) {
+ builder.addHeader("Accept", accept);
+ }
+ for (Entry keyValue : headerParams.entrySet()) {
+ builder.addHeader(keyValue.getKey(), keyValue.getValue());
+ }
+ for (Map.Entry keyValue : defaultHeaderMap.entrySet()) {
+ if (!headerParams.containsKey(keyValue.getKey())) {
+ builder.addHeader(keyValue.getKey(), keyValue.getValue());
+ }
+ }
+
+ BasicCookieStore store = new BasicCookieStore();
+ for (Entry keyValue : cookieParams.entrySet()) {
+ store.addCookie(buildCookie(keyValue.getKey(), keyValue.getValue(), builder.getUri()));
+ }
+ for (Entry keyValue : defaultCookieMap.entrySet()) {
+ if (!cookieParams.containsKey(keyValue.getKey())) {
+ store.addCookie(buildCookie(keyValue.getKey(), keyValue.getValue(), builder.getUri()));
+ }
+ }
+
+ HttpClientContext context = HttpClientContext.create();
+ context.setCookieStore(store);
+
+ ContentType contentTypeObj = getContentType(contentType);
+ if (body != null || !formParams.isEmpty()) {
+ if (isBodyAllowed(method)) {
+ // Add entity if we have content and a valid method
+ builder.setBody(serialize(body, formParams, contentTypeObj), contentTypeObj);
+ } else {
+ throw new ApiException("method " + method + " does not support a request body");
+ }
+ } else {
+ // for empty body
+ builder.setBody("", contentTypeObj);
+ }
+
+ CompletableFuture future = new CompletableFuture<>();
+ httpClient.execute(builder.build(), context, new FutureCallback() {
+ @Override
+ public void completed(SimpleHttpResponse result) {
+ try {
+ T data = processResponse(result, returnType);
+ future.complete(data);
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
+ }
+
+ @Override
+ public void failed(Exception ex) {
+ future.completeExceptionally(ex);
+ }
+
+ @Override
+ public void cancelled() {
+ future.cancel(true);
+ }
+ });
+
+ return future;
+ }
+
+ /**
+ * Update query and header parameters based on authentication settings.
+ *
+ * @param authNames The authentications to apply
+ * @param queryParams Query parameters
+ * @param headerParams Header parameters
+ * @param cookieParams Cookie parameters
+ */
+ private void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, Map cookieParams) {
+ for (String authName : authNames) {
+ Authentication auth = authentications.get(authName);
+ if (auth == null) throw new RuntimeException("Authentication undefined: " + authName);
+ auth.applyToParams(queryParams, headerParams, cookieParams);
+ }
+ }
+
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache
new file mode 100644
index 000000000000..0ba08c103017
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/BaseApi.mustache
@@ -0,0 +1,111 @@
+{{>licenseInfo}}
+package {{invokerPackage}};
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.Collections;
+import java.util.Map;
+
+{{>generatedAnnotation}}
+public abstract class BaseApi {
+
+ protected ApiClient apiClient;
+
+ public BaseApi() {
+ this(Configuration.getDefaultApiClient());
+ }
+
+ public BaseApi(ApiClient apiClient) {
+ this.apiClient = apiClient;
+ }
+
+ public ApiClient getApiClient() {
+ return apiClient;
+ }
+
+ public void setApiClient(ApiClient apiClient) {
+ this.apiClient = apiClient;
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method) throws ApiException {
+ return invokeAPI(url, method, null, null, Collections.emptyMap());
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param additionalHeaders Additional headers for the request.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method, Map additionalHeaders) throws ApiException {
+ return invokeAPI(url, method, null, null, additionalHeaders);
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param request The request object.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method, Object request) throws ApiException {
+ return invokeAPI(url, method, request, null, Collections.emptyMap());
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param request The request object.
+ * @param additionalHeaders Additional headers for the request.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method, Object request, Map additionalHeaders) throws ApiException {
+ return invokeAPI(url, method, request, null, additionalHeaders);
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param returnType The return type.
+ * @return The API response in the specified type.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method, TypeReference returnType) throws ApiException {
+ return invokeAPI(url, method, null, returnType, Collections.emptyMap());
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param request The request object.
+ * @param returnType The return type.
+ * @return The API response in the specified type.
+ * @throws ApiException if fails to make API call.
+ */
+ public CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType) throws ApiException {
+ return invokeAPI(url, method, request, returnType, Collections.emptyMap());
+ }
+
+ /**
+ * Directly invoke the API for the given URL. Useful if the API returns direct links/URLs for subsequent requests.
+ * @param url The URL for the request, either full URL or only the path.
+ * @param method The HTTP method for the request.
+ * @param request The request object.
+ * @param returnType The return type.
+ * @param additionalHeaders Additional headers for the request.
+ * @return The API response in the specified type.
+ * @throws ApiException if fails to make API call.
+ */
+ public abstract CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException;
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache
new file mode 100644
index 000000000000..b50e7db085c2
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/README.mustache
@@ -0,0 +1,209 @@
+# {{artifactId}}
+
+{{appName}}
+
+- API version: {{appVersion}}
+{{^hideGenerationTimestamp}}
+
+- Build date: {{generatedDate}}
+{{/hideGenerationTimestamp}}
+
+- Generator version: {{generatorVersion}}
+
+{{#appDescriptionWithNewLines}}{{{appDescriptionWithNewLines}}}{{/appDescriptionWithNewLines}}
+
+{{#infoUrl}}
+ For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
+{{/infoUrl}}
+
+*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)*
+
+## Requirements
+
+Building the API client library requires:
+
+1. Java 1.8+
+2. Maven/Gradle
+
+## Installation
+
+To install the API client library to your local Maven repository, simply execute:
+
+```shell
+mvn clean install
+```
+
+To deploy it to a remote Maven repository instead, configure the settings of the repository and execute:
+
+```shell
+mvn clean deploy
+```
+
+Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information.
+
+### Maven users
+
+Add this dependency to your project's POM:
+
+```xml
+
+ {{{groupId}}}
+ {{{artifactId}}}
+ {{{artifactVersion}}}
+ compile
+
+```
+
+### Gradle users
+
+Add this dependency to your project's build file:
+
+```groovy
+compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}"
+```
+
+### Others
+
+At first generate the JAR by executing:
+
+```shell
+mvn clean package
+```
+
+Then manually install the following JARs:
+
+- `target/{{{artifactId}}}-{{{artifactVersion}}}.jar`
+- `target/lib/*.jar`
+
+## Getting Started
+
+Please follow the [installation](#installation) instruction and execute the following Java code:
+
+```java
+{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}
+import {{{invokerPackage}}}.*;
+import {{{invokerPackage}}}.auth.*;
+import {{{modelPackage}}}.*;
+import {{{package}}}.{{{classname}}};
+
+public class {{{classname}}}Example {
+
+ public static void main(String[] args) {
+ ApiClient defaultClient = Configuration.getDefaultApiClient();
+ defaultClient.setBasePath("{{{basePath}}}");
+ {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
+ // Configure HTTP basic authorization: {{{name}}}
+ HttpBasicAuth {{{name}}} = (HttpBasicAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setUsername("YOUR USERNAME");
+ {{{name}}}.setPassword("YOUR PASSWORD");{{/isBasicBasic}}{{#isBasicBearer}}
+ // Configure HTTP bearer authorization: {{{name}}}
+ HttpBearerAuth {{{name}}} = (HttpBearerAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setBearerToken("BEARER TOKEN");{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}
+ // Configure API key authorization: {{{name}}}
+ ApiKeyAuth {{{name}}} = (ApiKeyAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setApiKey("YOUR API KEY");
+ // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null)
+ //{{{name}}}.setApiKeyPrefix("Token");{{/isApiKey}}{{#isOAuth}}
+ // Configure OAuth2 access token for authorization: {{{name}}}
+ OAuth {{{name}}} = (OAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setAccessToken("YOUR ACCESS TOKEN");{{/isOAuth}}{{#isHttpSignature}}
+ // Configure HTTP signature authorization: {{{name}}}
+ HttpSignatureAuth {{{name}}} = (HttpSignatureAuth) defaultClient.getAuthentication("{{{name}}}");
+ // All the HTTP signature parameters below should be customized to your environment.
+ // Configure the keyId
+ {{{name}}}.setKeyId("YOUR KEY ID");
+ // Configure the signature algorithm
+ {{{name}}}.setSigningAlgorithm(SigningAlgorithm.HS2019);
+ // Configure the specific cryptographic algorithm
+ {{{name}}}.setAlgorithm(Algorithm.ECDSA_SHA256);
+ // Configure the cryptographic algorithm parameters, if applicable
+ {{{name}}}.setAlgorithmParameterSpec(null);
+ // Set the cryptographic digest algorithm.
+ {{{name}}}.setDigestAlgorithm("SHA-256");
+ // Set the HTTP headers that should be included in the HTTP signature.
+ {{{name}}}.setHeaders(Arrays.asList("date", "host"));
+ // Set the private key used to sign the HTTP messages
+ {{{name}}}.setPrivateKey();{{/isHttpSignature}}
+ {{/authMethods}}
+ {{/hasAuthMethods}}
+
+ {{{classname}}} apiInstance = new {{{classname}}}(defaultClient);
+ {{#allParams}}
+ {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}}
+ {{/allParams}}
+ try {
+ {{#returnType}}{{{returnType}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}}
+ System.out.println(result);{{/returnType}}
+ } catch (ApiException e) {
+ System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}");
+ System.err.println("Status code: " + e.getCode());
+ System.err.println("Reason: " + e.getResponseBody());
+ System.err.println("Response headers: " + e.getResponseHeaders());
+ e.printStackTrace();
+ }
+ }
+}
+{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
+```
+
+## Documentation for API Endpoints
+
+All URIs are relative to *{{basePath}}*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{commonPath}}{{path}} | {{#summary}}{{summary}}{{/summary}}
+{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
+
+## Documentation for Models
+
+{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md)
+{{/model}}{{/models}}
+
+
+## Documentation for Authorization
+
+{{^authMethods}}Endpoints do not require authorization.{{/authMethods}}
+{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}}
+{{#authMethods}}
+
+### {{name}}
+
+{{#isApiKey}}
+
+- **Type**: API key
+- **API key parameter name**: {{keyParamName}}
+- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
+{{/isApiKey}}
+{{#isBasicBasic}}
+
+- **Type**: HTTP basic authentication
+{{/isBasicBasic}}
+{{#isBasicBearer}}
+
+- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}}
+{{/isBasicBearer}}
+{{#isHttpSignature}}
+
+- **Type**: HTTP signature authentication
+{{/isHttpSignature}}
+{{#isOAuth}}
+
+- **Type**: OAuth
+- **Flow**: {{flow}}
+- **Authorization URL**: {{authorizationUrl}}
+- **Scopes**: {{^scopes}}N/A{{/scopes}}
+{{#scopes}} - {{scope}}: {{description}}
+{{/scopes}}
+{{/isOAuth}}
+
+{{/authMethods}}
+
+## Recommendation
+
+It's recommended to create an instance of `ApiClient` per thread in a multithreaded environment to avoid any potential issues.
+
+## Author
+
+{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}}
+{{/-last}}{{/apis}}{{/apiInfo}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache
new file mode 100644
index 000000000000..9b2ff5c86e7e
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api.mustache
@@ -0,0 +1,240 @@
+{{>licenseInfo}}
+package {{package}};
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import {{invokerPackage}}.ApiException;
+import {{invokerPackage}}.ApiClient;
+import {{invokerPackage}}.BaseApi;
+import {{invokerPackage}}.Configuration;
+{{#models.0}}
+import {{modelPackage}}.*;
+{{/models.0}}
+import {{invokerPackage}}.Pair;
+
+{{#imports}}import {{import}};
+{{/imports}}
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.concurrent.CompletableFuture;
+
+{{#useBeanValidation}}
+import {{javaxPackage}}.validation.constraints.*;
+import {{javaxPackage}}.validation.Valid;
+
+{{/useBeanValidation}}
+{{>generatedAnnotation}}
+{{#operations}}
+public class {{classname}} extends BaseApi {
+
+ public {{classname}}() {
+ super(Configuration.getDefaultApiClient());
+ }
+
+ public {{classname}}(ApiClient apiClient) {
+ super(apiClient);
+ }
+
+ {{#operation}}
+ /**
+ * {{summary}}
+ * {{notes}}
+ {{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
+ {{/allParams}}
+ {{#returnType}}
+ * @return CompletableFuture<{{returnType}}>
+ {{/returnType}}
+ * @throws ApiException if fails to make API call
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public CompletableFuture<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException {
+ return this.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}Collections.emptyMap());
+ }
+
+
+ /**
+ * {{summary}}
+ * {{notes}}
+ {{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
+ {{/allParams}}
+ * @param additionalHeaders additionalHeaders for this call
+ {{#returnType}}
+ * @return CompletableFuture<{{returnType}}>
+ {{/returnType}}
+ * @throws ApiException if fails to make API call
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public CompletableFuture<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}Map additionalHeaders) throws ApiException {
+ Object localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
+ {{#allParams}}{{#required}}
+ // verify the required parameter '{{paramName}}' is set
+ if ({{paramName}} == null) {
+ throw new ApiException(400, "Missing the required parameter '{{paramName}}' when calling {{operationId}}");
+ }
+ {{/required}}{{/allParams}}
+ // create path and map variables
+ String localVarPath = "{{{path}}}"{{#pathParams}}
+ .replaceAll("\\{" + "{{baseName}}" + "\\}", apiClient.escapeString(apiClient.parameterToString({{{paramName}}}))){{/pathParams}};
+
+ StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
+ String localVarQueryParameterBaseName;
+ List localVarQueryParams = new ArrayList();
+ List localVarCollectionQueryParams = new ArrayList();
+ Map localVarHeaderParams = new HashMap();
+ Map localVarCookieParams = new HashMap();
+ Map localVarFormParams = new HashMap();
+
+ {{#queryParams}}
+ {{#isDeepObject}}
+ localVarQueryParameterBaseName = "{{{baseName}}}";
+ {{#isArray}}
+ for (int i=0; i < {{paramName}}.size(); i++) {
+ localVarQueryStringJoiner.add({{paramName}}.get(i).toUrlQueryString(String.format("{{baseName}}[%d]", i)));
+ }
+ {{/isArray}}
+ {{^isArray}}
+ localVarQueryStringJoiner.add({{paramName}}.toUrlQueryString("{{baseName}}"));
+ {{/isArray}}
+ {{/isDeepObject}}
+ {{^isDeepObject}}
+ {{#isExplode}}
+ {{#hasVars}}
+ {{#vars}}
+ {{#isArray}}
+ localVarQueryParams.addAll(apiClient.parameterToPairs("multi", "{{baseName}}", {{paramName}}.{{getter}}()));
+ {{/isArray}}
+ {{^isArray}}
+ localVarQueryParams.addAll(apiClient.parameterToPair("{{baseName}}", {{paramName}}.{{getter}}()));
+ {{/isArray}}
+ {{/vars}}
+ {{/hasVars}}
+ {{^hasVars}}
+ {{#isModel}}
+ localVarQueryStringJoiner.add({{paramName}}.toUrlQueryString());
+ {{/isModel}}
+ {{^isModel}}
+ {{#collectionFormat}}localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}}));
+ {{/isModel}}
+ {{/hasVars}}
+ {{/isExplode}}
+ {{^isExplode}}
+ {{#collectionFormat}}localVarCollectionQueryParams.addAll(apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}}));
+ {{/isExplode}}
+ {{/isDeepObject}}
+ {{/queryParams}}
+ {{#headerParams}}if ({{paramName}} != null)
+ localVarHeaderParams.put("{{baseName}}", apiClient.parameterToString({{paramName}}));
+ {{/headerParams}}
+
+ localVarHeaderParams.putAll(additionalHeaders);
+
+ {{#cookieParams}}if ({{paramName}} != null)
+ localVarCookieParams.put("{{baseName}}", apiClient.parameterToString({{paramName}}));
+ {{/cookieParams}}
+
+ {{#formParams}}if ({{paramName}} != null)
+ localVarFormParams.put("{{baseName}}", {{paramName}});
+ {{/formParams}}
+
+ final String[] localVarAccepts = {
+ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}
+ };
+ final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
+
+ final String[] localVarContentTypes = {
+ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}
+ };
+ final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);
+
+ String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}} };
+
+ {{#returnType}}
+ TypeReference<{{{returnType}}}> localVarReturnType = new TypeReference<{{{returnType}}}>() {};
+ {{/returnType}}
+ return apiClient.invokeAPI(
+ localVarPath,
+ "{{httpMethod}}",
+ localVarQueryParams,
+ localVarCollectionQueryParams,
+ localVarQueryStringJoiner.toString(),
+ localVarPostBody,
+ localVarHeaderParams,
+ localVarCookieParams,
+ localVarFormParams,
+ localVarAccept,
+ localVarContentType,
+ localVarAuthNames,
+ {{#returnType}}localVarReturnType{{/returnType}}{{^returnType}}null{{/returnType}}
+ );
+ }
+
+ {{#-last}}
+ @Override
+ public CompletableFuture invokeAPI(String url, String method, Object request, TypeReference returnType, Map additionalHeaders) throws ApiException {
+ String localVarPath = url.replace(apiClient.getBaseURL(), "");
+ StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
+ List localVarQueryParams = new ArrayList();
+ List localVarCollectionQueryParams = new ArrayList();
+ Map localVarHeaderParams = new HashMap();
+ Map localVarCookieParams = new HashMap();
+ Map localVarFormParams = new HashMap();
+
+ localVarHeaderParams.putAll(additionalHeaders);
+
+ final String[] localVarAccepts = {
+ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}
+ };
+ final String localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
+
+ final String[] localVarContentTypes = {
+ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}
+ };
+ final String localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);
+
+ String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}} };
+
+ return apiClient.invokeAPI(
+ localVarPath,
+ method,
+ localVarQueryParams,
+ localVarCollectionQueryParams,
+ localVarQueryStringJoiner.toString(),
+ request,
+ localVarHeaderParams,
+ localVarCookieParams,
+ localVarFormParams,
+ localVarAccept,
+ localVarContentType,
+ localVarAuthNames,
+ returnType
+ );
+ }
+ {{/-last}}
+ {{/operation}}
+}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache
new file mode 100644
index 000000000000..e2b46150259a
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/api_test.mustache
@@ -0,0 +1,58 @@
+{{>licenseInfo}}
+
+package {{package}};
+
+import {{invokerPackage}}.ApiException;
+{{#imports}}import {{import}};
+{{/imports}}
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.util.concurrent.CompletableFuture;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+{{#useBeanValidation}}
+import {{javaxPackage}}.validation.constraints.*;
+import {{javaxPackage}}.validation.Valid;
+
+{{/useBeanValidation}}
+/**
+ * API tests for {{classname}}
+ */
+@Disabled
+public class {{classname}}Test {
+
+ private final {{classname}} api = new {{classname}}();
+
+ {{#operations}}
+ {{#operation}}
+ /**
+ {{#summary}}
+ * {{summary}}
+ *
+ {{/summary}}
+ {{#notes}}
+ * {{notes}}
+ *
+ {{/notes}}
+ * @throws ApiException
+ * if the Api call fails
+ */
+ @Test
+ public void {{operationId}}Test() throws ApiException {
+ {{#allParams}}
+ {{{dataType}}} {{paramName}} = null;
+ {{/allParams}}
+ {{#returnType}}CompletableFuture<{{{returnType}}}> response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+
+ // TODO: test validations
+ }
+ {{/operation}}
+ {{/operations}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache
new file mode 100644
index 000000000000..f18581f08ba1
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/build.gradle.mustache
@@ -0,0 +1,145 @@
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+
+group = '{{groupId}}'
+version = '{{artifactVersion}}'
+
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.+'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+if(hasProperty('target') && target == 'android') {
+
+ apply plugin: 'com.android.library'
+ apply plugin: 'com.github.dcendents.android-maven'
+
+ android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 25
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ // Rename the aar correctly
+ libraryVariants.all { variant ->
+ variant.outputs.each { output ->
+ def outputFile = output.outputFile
+ if (outputFile != null && outputFile.name.endsWith('.aar')) {
+ def fileName = "${project.name}-${variant.baseName}-${version}.aar"
+ output.outputFile = new File(outputFile.parent, fileName)
+ }
+ }
+ }
+
+ dependencies {
+ provided "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
+ }
+ }
+
+ afterEvaluate {
+ android.libraryVariants.all { variant ->
+ def task = project.tasks.create "jar${variant.name.capitalize()}", Jar
+ task.description = "Create jar artifact for ${variant.name}"
+ task.dependsOn variant.javaCompile
+ task.from variant.javaCompile.destinationDirectory
+ task.destinationDirectory = project.file("${project.buildDir}/outputs/jar")
+ task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar"
+ artifacts.add('archives', task);
+ }
+ }
+
+ task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier = 'sources'
+ }
+
+ artifacts {
+ archives sourcesJar
+ }
+
+} else {
+
+ apply plugin: 'java'
+ apply plugin: 'maven-publish'
+
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+
+ publishing {
+ publications {
+ maven(MavenPublication) {
+ artifactId = '{{artifactId}}'
+
+ from components.java
+ }
+ }
+ }
+
+ task execute(type:JavaExec) {
+ main = System.getProperty('mainClass')
+ classpath = sourceSets.main.runtimeClasspath
+ }
+
+ task sourcesJar(type: Jar, dependsOn: classes) {
+ classifier = 'sources'
+ from sourceSets.main.allSource
+ }
+
+ task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+ }
+
+ artifacts {
+ archives sourcesJar
+ archives javadocJar
+ }
+}
+
+ext {
+ swagger_annotations_version = "1.6.6"
+ jackson_version = "2.17.1"
+ jackson_databind_version = "2.17.1"
+ {{#openApiNullable}}
+ jackson_databind_nullable_version = "0.2.6"
+ {{/openApiNullable}}
+ jakarta_annotation_version = "1.3.5"
+ httpclient_version = "5.1.3"
+ jodatime_version = "2.9.9"
+ junit_version = "4.13.2"
+}
+
+dependencies {
+ implementation "io.swagger:swagger-annotations:$swagger_annotations_version"
+ implementation "com.google.code.findbugs:jsr305:3.0.2"
+ implementation "org.apache.httpcomponents.client5:httpclient5:$httpclient_version"
+ implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version"
+ implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
+ {{#openApiNullable}}
+ implementation "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version"
+ {{/openApiNullable}}
+ {{#joda}}
+ implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version"
+ {{/joda}}
+ implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
+ implementation "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
+ testImplementation "junit:junit:$junit_version"
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache
new file mode 100644
index 000000000000..3a506ca3f828
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/apache-asynchttpclient/pom.mustache
@@ -0,0 +1,369 @@
+
+ 4.0.0
+ {{groupId}}
+ {{artifactId}}
+ jar
+ {{artifactId}}
+ {{artifactVersion}}
+ {{artifactUrl}}
+ {{artifactDescription}}
+
+ {{scmConnection}}
+ {{scmDeveloperConnection}}
+ {{scmUrl}}
+
+{{#parentOverridden}}
+
+ {{{parentGroupId}}}
+ {{{parentArtifactId}}}
+ {{{parentVersion}}}
+
+{{/parentOverridden}}
+
+
+
+ {{licenseName}}
+ {{licenseUrl}}
+ repo
+
+
+
+
+
+ {{developerName}}
+ {{developerEmail}}
+ {{developerOrganization}}
+ {{developerOrganizationUrl}}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+
+ 1.8
+ true
+ 128m
+ 512m
+
+ -Xlint:all
+ -J-Xss4m
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.1.0
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 2.2.0
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+
+ loggerPath
+ conf/log4j.properties
+
+
+ -Xms512m -Xmx1500m
+ methods
+ 10
+
+
+
+ maven-dependency-plugin
+
+
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/lib
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ test-jar
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.3.0
+
+
+ add_sources
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+ add_test_sources
+ generate-test-sources
+
+ add-test-source
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.4.1
+
+ none
+
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+
+
+
+ sign-artifacts
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.0.1
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+
+
+
+ {{#swagger1AnnotationLibrary}}
+
+ io.swagger
+ swagger-annotations
+ ${swagger-annotations-version}
+
+ {{/swagger1AnnotationLibrary}}
+ {{#swagger2AnnotationLibrary}}
+
+ io.swagger.core.v3
+ swagger-annotations
+ ${swagger-annotations-version}
+
+ {{/swagger2AnnotationLibrary}}
+
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${httpclient-version}
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson-version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson-version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson-databind-version}
+
+ {{^useJakartaEe}}
+
+ com.fasterxml.jackson.jaxrs
+ jackson-jaxrs-json-provider
+ ${jackson-version}
+
+ {{/useJakartaEe}}
+ {{#useJakartaEe}}
+
+ com.fasterxml.jackson.jakarta.rs
+ jackson-jakarta-rs-json-provider
+ ${jackson-version}
+
+ {{/useJakartaEe}}
+ {{#withXml}}
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson-version}
+
+
+ {{/withXml}}
+ {{#joda}}
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-joda
+ ${jackson-version}
+
+ {{/joda}}
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson-version}
+
+ {{#useBeanValidation}}
+
+
+ jakarta.validation
+ jakarta.validation-api
+ ${beanvalidation-version}
+ provided
+
+ {{/useBeanValidation}}
+ {{#performBeanValidation}}
+
+
+ org.hibernate
+ hibernate-validator
+ 5.4.3.Final
+
+ {{/performBeanValidation}}
+ {{#parcelableModel}}
+
+
+ com.google.android
+ android
+ 4.1.1.4
+ provided
+
+ {{/parcelableModel}}
+ {{#openApiNullable}}
+
+ org.openapitools
+ jackson-databind-nullable
+ ${jackson-databind-nullable-version}
+
+ {{/openApiNullable}}
+
+ jakarta.annotation
+ jakarta.annotation-api
+ ${jakarta-annotation-version}
+ provided
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-version}
+ test
+
+
+
+ UTF-8
+ {{#swagger1AnnotationLibrary}}
+ 1.6.6
+ {{/swagger1AnnotationLibrary}}
+ {{#swagger2AnnotationLibrary}}
+ 2.2.15
+ {{/swagger2AnnotationLibrary}}
+ 5.2.1
+ 2.17.1
+ 2.17.1
+ {{#openApiNullable}}
+ 0.2.6
+ {{/openApiNullable}}
+ {{#useJakartaEe}}
+ 2.1.1
+ 3.0.2
+ {{/useJakartaEe}}
+ {{^useJakartaEe}}
+ 1.3.5
+ 2.0.2
+ {{/useJakartaEe}}
+ 5.10.2
+
+
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
index 71191d9f7194..a63f4d4b0b35 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
@@ -72,6 +72,7 @@ public class JavaClientCodegenTest {
@Getter
enum Library {
APACHE_HTTPCLIENT("apache-httpclient", Serializer.JACKSON),
+ APACHE_ASYNCHTTPCLIENT("apache-asynchttpclient", Serializer.JACKSON),
FEIGN("feign", Serializer.JACKSON, Set.of(Serializer.GSON)),
GOOGLE_API_CLIENT("google-api-client", Serializer.JACKSON),
JERSEY_2("jersey2", Serializer.JACKSON),
@@ -3234,4 +3235,24 @@ public void testGenerateParameterId() {
" getCall(Integer queryParameter, final ApiCallback _callback)"
);
}
+
+ @Test
+ public void testApacheAsyncHasCompletableFuture() {
+ final Path output = newTempFolder();
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setGeneratorName("java")
+ .setLibrary("apache-asynchttpclient")
+ .setInputSpec("src/test/resources/3_0/echo_api.yaml")
+ .setOutputDir(output.toString().replace("\\", "/"));
+
+ new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
+
+ Path apiFile = output.resolve("src/main/java/org/openapitools/client/api/QueryApi.java");
+
+ assertThat(apiFile).content()
+ .contains("import java.util.concurrent.CompletableFuture;")
+ .contains("CompletableFuture invokeAPI")
+ .contains("CompletableFuture testQueryStyleFormExplodeTrueObjectAllOf(");
+ }
+
}
\ No newline at end of file