> getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Get the data.
+ *
+ * @return the data
+ */
+ public T getData() {
+ return data;
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/GzipRequestInterceptor.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/GzipRequestInterceptor.mustache
new file mode 100644
index 000000000000..bcaccfd1cb36
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/GzipRequestInterceptor.mustache
@@ -0,0 +1,128 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import okhttp3.*;
+import okio.Buffer;
+import okio.BufferedSink;
+import okio.GzipSink;
+import okio.Okio;
+
+import java.io.IOException;
+
+/**
+ * Encodes request bodies using gzip.
+ *
+ * Taken from https://github.com/square/okhttp/issues/350
+ */
+class GzipRequestInterceptor implements Interceptor {
+
+ /**
+ * Number of bytes handed to the sink per write while replaying a buffered body. Replaying the
+ * whole body in a single write would collapse an upload into one progress event, so it is
+ * replayed one okio segment at a time instead.
+ */
+ private static final long WRITE_CHUNK_SIZE = 8192L;
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request originalRequest = chain.request();
+ RequestBody body = originalRequest.body();
+ if (body == null || originalRequest.header("Content-Encoding") != null || body.contentLength() == 0) {
+ return chain.proceed(originalRequest);
+ }
+ {{#withAWSV4Signature}}
+
+ // An AWS SigV4 signature commits to a hash of the request payload, so a signed body cannot
+ // be re-encoded afterwards: gzipping it here would make the transmitted bytes disagree with
+ // the signature and AWS would reject the request. Send signed bodies uncompressed instead.
+ String authorization = originalRequest.header("Authorization");
+ if (authorization != null && authorization.startsWith("AWS4-HMAC-SHA256")) {
+ return chain.proceed(originalRequest);
+ }
+ {{/withAWSV4Signature}}
+
+ Request compressedRequest = originalRequest.newBuilder()
+ .header("Content-Encoding", "gzip")
+ .method(originalRequest.method(), compress(body))
+ .build();
+ return chain.proceed(compressedRequest);
+ }
+
+ /**
+ * Compresses the request body while keeping upload progress reporting truthful.
+ *
+ * Compressing drains the body up front (see {@link #forceContentLength}). Draining a
+ * {@link ProgressRequestBody} here would fire every upload callback, the terminal one included,
+ * before a single byte reached the socket, and would report the uncompressed sizes. So the body
+ * that the {@code ProgressRequestBody} wraps is compressed instead, and the compressed bytes are
+ * re-wrapped with the same callback: progress is then reported against the bytes that are
+ * actually transmitted, as they are transmitted.
+ */
+ private RequestBody compress(final RequestBody body) throws IOException {
+ if (body instanceof ProgressRequestBody) {
+ ProgressRequestBody progressBody = (ProgressRequestBody) body;
+ return new ProgressRequestBody(forceContentLength(gzip(progressBody.getDelegate())), progressBody.getCallback());
+ }
+ return forceContentLength(gzip(body));
+ }
+
+ /**
+ * The gzip body reports an unknown content length, which makes OkHttp fall back to
+ * "Transfer-Encoding: chunked". Servers that reject chunked request bodies then fail every
+ * compressed request, so buffer the compressed bytes and republish their known length.
+ *
+ *
Known limitation: the compressed body is held in memory until the request completes, so a
+ * very large upload can exhaust the heap. Publishing the compressed length requires compressing
+ * the whole body first, so the buffering is inherent to the behaviour above; clients that upload
+ * bodies too large to buffer should leave gzip request compression disabled.
+ */
+ private RequestBody forceContentLength(final RequestBody requestBody) throws IOException {
+ final Buffer buffer = new Buffer();
+ requestBody.writeTo(buffer);
+ return new RequestBody() {
+ @Override
+ public MediaType contentType() {
+ return requestBody.contentType();
+ }
+
+ @Override
+ public long contentLength() {
+ return buffer.size();
+ }
+
+ @Override
+ public void writeTo(BufferedSink sink) throws IOException {
+ // clone() shares the buffered segments instead of copying them, so the body stays
+ // replayable (OkHttp may re-send it after a redirect or an auth challenge) without
+ // a second copy of the payload. Write it out in chunks so that an enclosing
+ // ProgressRequestBody sees the upload advance rather than one all-at-once event.
+ Buffer source = buffer.clone();
+ while (!source.exhausted()) {
+ sink.write(source, Math.min(source.size(), WRITE_CHUNK_SIZE));
+ }
+ }
+ };
+ }
+
+ private RequestBody gzip(final RequestBody body) {
+ return new RequestBody() {
+ @Override
+ public MediaType contentType() {
+ return body.contentType();
+ }
+
+ @Override
+ public long contentLength() {
+ return -1; // We don't know the compressed length in advance!
+ }
+
+ @Override
+ public void writeTo(BufferedSink sink) throws IOException {
+ BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
+ body.writeTo(gzipSink);
+ gzipSink.close();
+ }
+ };
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/JSON.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/JSON.mustache
new file mode 100644
index 000000000000..96347c1c9a4f
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/JSON.mustache
@@ -0,0 +1,1801 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import {{modelPackage}}.AbstractOpenApiSchema;
+
+{{#isGson}}
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.bind.util.ISO8601Utils;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.google.gson.JsonElement;
+import io.gsonfire.GsonFireBuilder;
+import io.gsonfire.TypeSelector;
+{{/isGson}}
+{{#isJackson}}
+import {{jacksonPackage}}.core.JsonGenerator;
+import {{jacksonPackage}}.core.JsonParser;
+import {{jacksonPackage}}.core.JsonToken;
+{{#useJackson3}}
+import {{jacksonPackage}}.core.JacksonException;
+import {{jacksonPackage}}.core.json.JsonReadFeature;
+{{/useJackson3}}
+import com.fasterxml.jackson.annotation.*;
+import {{jacksonPackage}}.databind.*;
+import {{jacksonPackage}}.databind.json.JsonMapper;
+import {{jacksonPackage}}.databind.module.SimpleModule;
+{{^useJackson3}}
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+{{/useJackson3}}
+{{#useJackson3}}
+import {{jacksonPackage}}.databind.cfg.DateTimeFeature;
+import {{jacksonPackage}}.databind.cfg.EnumFeature;
+{{/useJackson3}}
+{{#openApiNullable}}
+import org.openapitools.jackson.nullable.{{#useJackson3}}JsonNullableJackson3Module{{/useJackson3}}{{^useJackson3}}JsonNullableModule{{/useJackson3}};
+{{/openApiNullable}}
+{{/isJackson}}
+{{#isJsonb}}
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.io.File;
+{{/isJsonb}}
+
+{{#joda}}
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+{{#isGson}}
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.DateTimeFormatterBuilder;
+import org.joda.time.format.ISODateTimeFormat;
+{{/isGson}}
+{{#isJsonb}}
+{{! Required: the JSON-B joda adapters below, their format fields and their setters are all
+ typed on joda's DateTimeFormatter, and java.time's is only imported for the jsr310 date
+ libraries. Without these two imports JSON.java references DateTimeFormatter in 8 places
+ with no import at all and does not compile. }}
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+{{/isJsonb}}
+{{#isJackson}}
+import {{jacksonPackage}}.datatype.joda.JodaModule;
+{{/isJackson}}
+{{/joda}}
+
+import okio.ByteString;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+{{#jsr310}}
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+{{/jsr310}}
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Locale;
+import java.util.Map;
+import java.util.HashMap;
+
+/*
+ * A JSON utility class
+ */
+public class JSON {
+ {{#isGson}}
+ private static Gson gson;
+ private static boolean isLenientOnJson = false;
+ private static DateTypeAdapter dateTypeAdapter = new DateTypeAdapter();
+ private static SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter();
+ {{#joda}}
+ private static DateTimeTypeAdapter dateTimeTypeAdapter = new DateTimeTypeAdapter();
+ private static LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter();
+ {{/joda}}
+ {{#jsr310}}
+ private static OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter();
+ private static LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter();
+ private static LocalDateTimeTypeAdapter localDateTimeTypeAdapter = new LocalDateTimeTypeAdapter();
+ {{/jsr310}}
+ private static ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter();
+ {{/isGson}}
+ {{#isJackson}}
+ private static ObjectMapper mapper;
+ {{/isJackson}}
+ {{#isJsonb}}
+ {{! volatile: rebuildJsonb() swaps the instances at runtime (date-format setters) while
+ OkHttp dispatcher threads may be reading them. }}
+ private static volatile Jsonb jsonb;
+ private static volatile Jsonb plainJsonb;
+ {{! A Jsonb instance is immutable, so the configured formats are kept here and the instance is
+ rebuilt from scratch whenever one of them changes. }}
+ private static DateFormat dateFormat;
+ private static DateFormat sqlDateFormat;
+ {{#jsr310}}
+ private static DateTimeFormatter offsetDateTimeFormat;
+ private static DateTimeFormatter localDateFormat;
+ private static DateTimeFormatter localDateTimeFormat;
+ {{/jsr310}}
+ {{#joda}}
+ private static DateTimeFormatter dateTimeFormat;
+ private static DateTimeFormatter jodaLocalDateFormat;
+ {{/joda}}
+ {{/isJsonb}}
+
+ public JSON() {
+ {{#isGson}}
+ if (gson != null) {
+ // the shared static Gson is already built; rebuilding would discard a
+ // customization installed through setGson()
+ return;
+ }
+ GsonFireBuilder fireBuilder = new GsonFireBuilder();
+ {{#models}}
+ {{#model}}
+ {{#discriminator}}
+ fireBuilder.registerTypeSelector({{modelPackage}}.{{classname}}.class, new TypeSelector<{{modelPackage}}.{{classname}}>() {
+ @Override
+ public Class extends {{modelPackage}}.{{classname}}> getClassForElement(JsonElement readElement) {
+ Map classByDiscriminatorValue = new HashMap();
+ {{#mappedModels}}
+ classByDiscriminatorValue.put("{{mappingName}}"{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}}, {{modelPackage}}.{{modelName}}.class);
+ {{/mappedModels}}
+ classByDiscriminatorValue.put("{{name}}"{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}}, {{modelPackage}}.{{classname}}.class);
+ return getClassByDiscriminator(classByDiscriminatorValue,
+ getDiscriminatorValue(readElement, "{{{propertyBaseName}}}"));
+ }
+ });
+ {{/discriminator}}
+ {{/model}}
+ {{/models}}
+ GsonBuilder builder = fireBuilder.createGsonBuilder();
+ {{#disableHtmlEscaping}}
+ builder.disableHtmlEscaping();
+ {{/disableHtmlEscaping}}
+ builder.registerTypeAdapter(Date.class, dateTypeAdapter);
+ builder.registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter);
+ {{#joda}}
+ builder.registerTypeAdapter(DateTime.class, dateTimeTypeAdapter);
+ builder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter);
+ {{/joda}}
+ {{#jsr310}}
+ builder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter);
+ builder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter);
+ builder.registerTypeAdapter(LocalDateTime.class, localDateTimeTypeAdapter);
+ {{/jsr310}}
+ builder.registerTypeAdapter(byte[].class, byteArrayAdapter);
+ {{#models}}
+ {{#model}}
+ {{^isEnum}}
+ {{^hasChildren}}
+ builder.registerTypeAdapterFactory(new {{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory());
+ {{/hasChildren}}
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ gson = builder.create();
+ {{/isGson}}
+ {{#isJackson}}
+ if (mapper == null) {
+ {{^useJackson3}}
+ mapper = JsonMapper.builder()
+ .serializationInclusion(JsonInclude.Include.NON_NULL)
+ .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
+ {{#failOnUnknownProperties}}
+ .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ {{/failOnUnknownProperties}}
+ {{^failOnUnknownProperties}}
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ {{/failOnUnknownProperties}}
+ .enable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
+ .enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
+ .defaultDateFormat(new RFC3339DateFormat())
+ .addModule(new JavaTimeModule())
+ .addModule(rfc3339JavaTimeModule())
+ .build();
+ {{#joda}}
+ mapper.registerModule(new JodaModule());
+ {{/joda}}
+ {{#openApiNullable}}
+ mapper.registerModule(new JsonNullableModule());
+ {{/openApiNullable}}
+ {{/useJackson3}}
+ {{#useJackson3}}
+ JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder()
+ .changeDefaultPropertyInclusion(v -> v.withValueInclusion(JsonInclude.Include.NON_NULL))
+ .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
+ {{#failOnUnknownProperties}}
+ .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ {{/failOnUnknownProperties}}
+ {{^failOnUnknownProperties}}
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ {{/failOnUnknownProperties}}
+ .enable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
+ .disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .enable(EnumFeature.WRITE_ENUMS_USING_TO_STRING)
+ .enable(EnumFeature.READ_ENUMS_USING_TO_STRING)
+ .defaultDateFormat(new RFC3339DateFormat());
+ jsonMapperBuilder.addModule(new RFC3339JavaTimeModule());
+ {{#joda}}
+ jsonMapperBuilder.addModule(new JodaModule());
+ {{/joda}}
+ {{#openApiNullable}}
+ jsonMapperBuilder.addModule(new JsonNullableJackson3Module());
+ {{/openApiNullable}}
+ mapper = jsonMapperBuilder.build();
+ {{/useJackson3}}
+ }
+ {{/isJackson}}
+ {{#isJsonb}}
+ {{! plainJsonb is checked too: setJsonb()/setSerializer() may have installed a custom
+ instance before the first JSON was constructed, and the models delegate through the
+ companion instance regardless of which instance serves the application. }}
+ if (jsonb == null || plainJsonb == null) {
+ rebuildJsonb();
+ }
+ {{/isJsonb}}
+ }
+
+ {{#isGson}}
+ private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) {
+ JsonElement element = readElement.getAsJsonObject().get(discriminatorField);
+ if (null == element) {
+ throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">");
+ }
+ return element.getAsString();
+ }
+
+ private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) {
+ Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue{{^discriminatorCaseSensitive}}.toUpperCase(Locale.ROOT){{/discriminatorCaseSensitive}});
+ if (null == clazz) {
+ throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">");
+ }
+ return clazz;
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ {{^useJackson3}}
+ {{! Deliberately NOT `new RFC3339JavaTimeModule()`. That class is a shared root template,
+ byte-identical to master, and on Jackson 2 it registers its deserializers from
+ setupModule() only AFTER calling super.setupModule(context). SimpleModule forwards
+ _deserializers to the context just once, and that map is created lazily by the first
+ addDeserializer call - so on Jackson 2 the registrations are lost entirely and the
+ lenient RFC3339 parsing never takes effect (e.g. "2020-01-01 12:00:00.000+02:00", with
+ a space instead of T, is rejected instead of normalised). Jackson 3 is unaffected
+ because its constructor does the registering. Building the equivalent module here fixes
+ the okhttp library without touching a template shared by six libraries; the upstream fix
+ belongs in RFC3339JavaTimeModule itself. }}
+ private static SimpleModule rfc3339JavaTimeModule() {
+ SimpleModule module = new SimpleModule("RFC3339JavaTimeModule");
+ module.addDeserializer(java.time.Instant.class, RFC3339InstantDeserializer.INSTANT);
+ module.addDeserializer(java.time.OffsetDateTime.class, RFC3339InstantDeserializer.OFFSET_DATE_TIME);
+ module.addDeserializer(java.time.ZonedDateTime.class, RFC3339InstantDeserializer.ZONED_DATE_TIME);
+ return module;
+ }
+
+ {{/useJackson3}}
+ /**
+ * Returns the target model class that should be used to deserialize the input data.
+ * The discriminator mappings are used to determine the target model class.
+ *
+ * @param node The input data.
+ * @param modelClass The class that contains the discriminator mappings.
+ * @return the target model class.
+ */
+ public static Class> getClassForElement(JsonNode node, Class> modelClass) {
+ ClassDiscriminatorMapping cdm = modelDiscriminators.get(modelClass);
+ if (cdm != null) {
+ return cdm.getClassForElement(node, new HashSet>());
+ }
+ return null;
+ }
+
+ /**
+ * Helper class to register the discriminator mappings.
+ */
+ private static class ClassDiscriminatorMapping {
+ // The model class name.
+ Class> modelClass;
+ // The name of the discriminator property.
+ String discriminatorName;
+ // The discriminator mappings for a model class.
+ Map> discriminatorMappings;
+
+ // Constructs a new class discriminator.
+ ClassDiscriminatorMapping(Class> cls, String propertyName, Map> mappings) {
+ modelClass = cls;
+ discriminatorName = propertyName;
+ discriminatorMappings = new HashMap>();
+ if (mappings != null) {
+ discriminatorMappings.putAll(mappings);
+ }
+ }
+
+ // Return the name of the discriminator property for this model class.
+ String getDiscriminatorPropertyName() {
+ return discriminatorName;
+ }
+
+ // Return the discriminator value or null if the discriminator is not
+ // present in the payload.
+ String getDiscriminatorValue(JsonNode node) {
+ // Determine the value of the discriminator property in the input data.
+ if (discriminatorName != null) {
+ // Get the value of the discriminator property, if present in the input payload.
+ node = node.get(discriminatorName);
+ if (node != null && node.isValueNode()) {
+ {{^useJackson3}}
+ String discrValue = node.asText();
+ {{/useJackson3}}
+ {{#useJackson3}}
+ String discrValue = node.asString();
+ {{/useJackson3}}
+ if (discrValue != null) {
+ return discrValue;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the target model class that should be used to deserialize the input data.
+ * This function can be invoked for anyOf/oneOf composed models with discriminator mappings.
+ * The discriminator mappings are used to determine the target model class.
+ *
+ * @param node The input data.
+ * @param visitedClasses The set of classes that have already been visited.
+ * @return the target model class.
+ */
+ Class> getClassForElement(JsonNode node, Set> visitedClasses) {
+ if (visitedClasses.contains(modelClass)) {
+ // Class has already been visited.
+ return null;
+ }
+ // Determine the value of the discriminator property in the input data.
+ String discrValue = getDiscriminatorValue(node);
+ if (discrValue == null) {
+ return null;
+ }
+ Class> cls = discriminatorMappings.get(discrValue);
+ // It may not be sufficient to return this cls directly because that target class
+ // may itself be a composed schema, possibly with its own discriminator.
+ visitedClasses.add(modelClass);
+ for (Class> childClass : discriminatorMappings.values()) {
+ ClassDiscriminatorMapping childCdm = modelDiscriminators.get(childClass);
+ if (childCdm == null) {
+ continue;
+ }
+ if (!discriminatorName.equals(childCdm.discriminatorName)) {
+ discrValue = getDiscriminatorValue(node);
+ if (discrValue == null) {
+ continue;
+ }
+ }
+ if (childCdm != null) {
+ // Recursively traverse the discriminator mappings.
+ Class> childDiscr = childCdm.getClassForElement(node, visitedClasses);
+ if (childDiscr != null) {
+ return childDiscr;
+ }
+ }
+ }
+ return cls;
+ }
+ }
+
+ /**
+ * Returns true if inst is an instance of modelClass in the OpenAPI model hierarchy.
+ *
+ * The Java class hierarchy is not implemented the same way as the OpenAPI model hierarchy,
+ * so it's not possible to use the instanceof keyword.
+ *
+ * @param modelClass A OpenAPI model class.
+ * @param inst The instance object.
+ * @param visitedClasses The set of classes that have already been visited.
+ * @return true if inst is an instance of modelClass in the OpenAPI model hierarchy.
+ */
+ public static boolean isInstanceOf(Class> modelClass, Object inst, Set> visitedClasses) {
+ if (modelClass.isInstance(inst)) {
+ // This handles the 'allOf' use case with single parent inheritance.
+ return true;
+ }
+ if (visitedClasses.contains(modelClass)) {
+ // This is to prevent infinite recursion when the composed schemas have
+ // a circular dependency.
+ return false;
+ }
+ visitedClasses.add(modelClass);
+
+ // Traverse the oneOf/anyOf composed schemas.
+ Map> descendants = modelDescendants.get(modelClass);
+ if (descendants != null) {
+ for (Class> childType : descendants.values()) {
+ if (isInstanceOf(childType, inst, visitedClasses)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A map of discriminators for all model classes.
+ */
+ private static Map, ClassDiscriminatorMapping> modelDiscriminators = new HashMap<>();
+
+ /**
+ * A map of oneOf/anyOf descendants for each model class.
+ */
+ private static Map, Map>> modelDescendants = new HashMap<>();
+
+ /**
+ * Register a model class discriminator.
+ *
+ * @param modelClass the model class
+ * @param discriminatorPropertyName the name of the discriminator property
+ * @param mappings a map with the discriminator mappings.
+ */
+ public static void registerDiscriminator(Class> modelClass, String discriminatorPropertyName, Map> mappings) {
+ ClassDiscriminatorMapping m = new ClassDiscriminatorMapping(modelClass, discriminatorPropertyName, mappings);
+ modelDiscriminators.put(modelClass, m);
+ }
+
+ /**
+ * Register the oneOf/anyOf descendants of the modelClass.
+ *
+ * @param modelClass the model class
+ * @param descendants a map of oneOf/anyOf descendants.
+ */
+ public static void registerDescendants(Class> modelClass, Map> descendants) {
+ modelDescendants.put(modelClass, descendants);
+ }
+ {{/isJackson}}
+
+ /**
+ * Get the internal serialization object.
+ *
+ * @return serialization object
+ */
+ public static Object getSerializer() {
+ {{#isGson}}
+ return gson;
+ {{/isGson}}
+ {{#isJackson}}
+ return mapper;
+ {{/isJackson}}
+ {{#isJsonb}}
+ return jsonb;
+ {{/isJsonb}}
+ }
+
+ /**
+ * Set the internal serialization object.
+ *
+ * @param serializer serialization object
+ */
+ public static void setSerializer(Object serializer) {
+ {{#isGson}}
+ JSON.gson = (Gson) serializer;
+ {{/isGson}}
+ {{#isJackson}}
+ JSON.mapper = (ObjectMapper) serializer;
+ {{/isJackson}}
+ {{#isJsonb}}
+ setJsonb((Jsonb) serializer);
+ {{/isJsonb}}
+ }
+
+ {{#isJsonb}}
+ /**
+ * Helper class to get the Type of a generic class.
+ */
+ public static class GenericType {
+ private final Type type;
+
+ protected GenericType() {
+ Type superclass = getClass().getGenericSuperclass();
+ this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ }
+
+ public Type getType() {
+ return type;
+ }
+ }
+ {{/isJsonb}}
+ {{#isGson}}
+ public static Gson getGson() {
+ return gson;
+ }
+
+ public static void setGson(Gson gson) {
+ JSON.gson = gson;
+ }
+
+ public static void setLenientOnJson(boolean lenientOnJson) {
+ JSON.isLenientOnJson = lenientOnJson;
+ }
+
+ public static void setDateFormat(DateFormat dateFormat) {
+ dateTypeAdapter.setFormat(dateFormat);
+ }
+
+ public static void setSqlDateFormat(DateFormat dateFormat) {
+ sqlDateTypeAdapter.setFormat(dateFormat);
+ }
+
+ public static boolean isInstanceOf(Class> type, Object instance, Set> visited) {
+ if (instance == null) {
+ // null never matches a concrete schema type; nullable composed schemas accept
+ // null in setActualInstance before consulting this method
+ return false;
+ }
+ if (type.isInstance(instance)) {
+ return true;
+ }
+ if (instance instanceof AbstractOpenApiSchema) {
+ AbstractOpenApiSchema schema = (AbstractOpenApiSchema) instance;
+ visited.add(schema.getClass());
+ for (Class> allowedType : schema.getSchemas().values()) {
+ if (visited.contains(allowedType)) {
+ continue;
+ }
+ if (isInstanceOf(type, schema.getActualInstance(), visited)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ {{/isGson}}
+
+ {{#isJackson}}
+ public static ObjectMapper getMapper() {
+ return mapper;
+ }
+
+ public static void setMapper(ObjectMapper mapper) {
+ JSON.mapper = mapper;
+ }
+
+ public static void setDateFormat(DateFormat dateFormat) {
+ {{^useJackson3}}
+ mapper.setDateFormat(dateFormat);
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild().defaultDateFormat(dateFormat).build();
+ {{/useJackson3}}
+ }
+
+ public static void setSqlDateFormat(DateFormat dateFormat) {
+ {{^useJackson3}}
+ mapper.registerModule(new SimpleModule().addSerializer(java.sql.Date.class, new JsonSerializer() {
+ @Override
+ public void serialize(java.sql.Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateFormat.format(value));
+ }
+ }).addDeserializer(java.sql.Date.class, new JsonDeserializer() {
+ @Override
+ public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ try {
+ return new java.sql.Date(dateFormat.parse(p.getText()).getTime());
+ } catch (java.text.ParseException e) {
+ throw new IOException(e);
+ }
+ }
+ }));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .addModule(new SimpleModule().addSerializer(java.sql.Date.class, new ValueSerializer() {
+ @Override
+ public void serialize(java.sql.Date value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateFormat.format(value));
+ }
+ }).addDeserializer(java.sql.Date.class, new ValueDeserializer() {
+ @Override
+ public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ try {
+ return new java.sql.Date(dateFormat.parse(p.getString()).getTime());
+ } catch (java.text.ParseException e) {
+ throw new IllegalArgumentException("Failed to parse java.sql.Date", e);
+ }
+ }
+ }))
+ .build();
+ {{/useJackson3}}
+ }
+
+ public static void setLenientOnJson(boolean lenientOnJson) {
+ {{^useJackson3}}
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, lenientOnJson);
+ mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, lenientOnJson);
+ mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, lenientOnJson);
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .configure(JsonReadFeature.ALLOW_UNQUOTED_PROPERTY_NAMES, lenientOnJson)
+ .configure(JsonReadFeature.ALLOW_SINGLE_QUOTES, lenientOnJson)
+ .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, lenientOnJson)
+ .build();
+ {{/useJackson3}}
+ }
+ {{/isJackson}}
+
+ {{#isJsonb}}
+ public static Jsonb getJsonb() {
+ return jsonb;
+ }
+
+ /**
+ * The Jsonb instance without the polymorphism (de)serializers of the discriminated
+ * hierarchy roots. The generated model classes delegate concrete subtype binding through
+ * it; application code should use {@link #getJsonb()}.
+ *
+ * @return the polymorphism-free Jsonb instance
+ */
+ public static Jsonb getPlainJsonb() {
+ return plainJsonb;
+ }
+
+ /**
+ * Set the Jsonb instance the application (de)serializes through.
+ *
+ * The internal companion instance the generated models delegate through is built first
+ * if it does not exist yet, so a custom instance may be installed before the first
+ * {@link JSON} is constructed.
+ *
+ * @param jsonb the Jsonb instance to use
+ */
+ public static void setJsonb(Jsonb jsonb) {
+ if (plainJsonb == null) {
+ rebuildJsonb();
+ }
+ JSON.jsonb = jsonb;
+ }
+
+ /**
+ * Rebuild the Jsonb instance from the currently configured formats.
+ *
+ * A Jsonb instance cannot be reconfigured after it is built, so every format setter
+ * records its formatter and calls this method. Any instance previously supplied through
+ * {@link #setJsonb(Jsonb)} is replaced.
+ */
+ private static void rebuildJsonb() {
+ JsonbConfig config = new JsonbConfig();
+ {{! Unconditional: unlike the date adapters below there is no format to configure, and
+ without it every model carrying a 'format: binary' property fails to (de)serialize. }}
+ config.withAdapters(new FileAdapter());
+ if (dateFormat != null) {
+ config.withAdapters(new DateAdapter(dateFormat));
+ }
+ if (sqlDateFormat != null) {
+ config.withAdapters(new SqlDateAdapter(sqlDateFormat));
+ }
+ {{#jsr310}}
+ if (offsetDateTimeFormat != null) {
+ config.withAdapters(new OffsetDateTimeAdapter(offsetDateTimeFormat));
+ }
+ if (localDateFormat != null) {
+ config.withAdapters(new LocalDateAdapter(localDateFormat));
+ }
+ if (localDateTimeFormat != null) {
+ config.withAdapters(new LocalDateTimeAdapter(localDateTimeFormat));
+ }
+ {{/jsr310}}
+ {{#joda}}
+ {{! Registered unconditionally, mirroring the FileAdapter: Yasson has no built-in support
+ for joda types, so leaving these out until the application calls a format setter left
+ DateTime/LocalDate to plain bean mapping. Defaults are the ISO forms that match what
+ the Jackson JodaModule and the Gson joda type adapters produce. }}
+ config.withAdapters(new JodaDateTimeAdapter(
+ dateTimeFormat != null ? dateTimeFormat : ISODateTimeFormat.dateTime()));
+ config.withAdapters(new JodaLocalDateAdapter(
+ jodaLocalDateFormat != null ? jodaLocalDateFormat : ISODateTimeFormat.date()));
+ {{/joda}}
+ {{! JSON-B has no any-getter/any-setter, so models with 'additionalProperties: true'
+ round-trip their undeclared fields through custom (de)serializers generated into
+ the model. The guards mirror model.mustache's dispatch: only plain pojos define
+ these inner classes. }}
+ {{#models}}
+ {{#model}}
+ {{^isEnum}}
+ {{^vendorExtensions.x-is-one-of-interface}}
+ {{^oneOf}}
+ {{^anyOf}}
+ {{^hasChildren}}
+ {{#isAdditionalPropertiesTrue}}
+ config.withSerializers(new {{modelPackage}}.{{classname}}.CustomJsonbSerializer());
+ config.withDeserializers(new {{modelPackage}}.{{classname}}.CustomJsonbDeserializer());
+ {{/isAdditionalPropertiesTrue}}
+ {{/hasChildren}}
+ {{/anyOf}}
+ {{/oneOf}}
+ {{/vendorExtensions.x-is-one-of-interface}}
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ {{! A model that forbids additional properties binds by plain bean mapping, which drops
+ an undeclared key silently and ignores whether a required property is there at all;
+ these deserializers reject such a JSON object. They bind property by property
+ through the fully configured instance, so registering them here - on the companion
+ instance too - is what keeps a NESTED model's own checks in force. }}
+ {{#models}}
+ {{#model}}
+ {{^isEnum}}
+ {{^vendorExtensions.x-is-one-of-interface}}
+ {{^oneOf}}
+ {{^anyOf}}
+ {{^hasChildren}}
+ {{^isAdditionalPropertiesTrue}}
+ {{#allVars}}
+ {{#-first}}
+ config.withDeserializers(new {{modelPackage}}.{{classname}}.CustomJsonbDeserializer());
+ {{/-first}}
+ {{/allVars}}
+ {{/isAdditionalPropertiesTrue}}
+ {{/hasChildren}}
+ {{/anyOf}}
+ {{/oneOf}}
+ {{/vendorExtensions.x-is-one-of-interface}}
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ {{! Built before the polymorphism components are added: the root (de)serializers of the
+ discriminated hierarchies bind concrete subtypes through this instance, which keeps
+ them from recursing into themselves. Everything else is already registered, so a
+ subtype bound through it still enforces its own required and undeclared fields. }}
+ plainJsonb = JsonbBuilder.create(config);
+ {{#models}}
+ {{#model}}
+ {{^isEnum}}
+ {{#hasChildren}}
+ {{#discriminator}}
+ config.withSerializers(new {{modelPackage}}.{{classname}}.CustomJsonbSerializer());
+ config.withDeserializers(new {{modelPackage}}.{{classname}}.CustomJsonbDeserializer());
+ {{/discriminator}}
+ {{/hasChildren}}
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ jsonb = JsonbBuilder.create(config);
+ }
+
+ public static void setDateFormat(DateFormat dateFormat) {
+ JSON.dateFormat = dateFormat;
+ rebuildJsonb();
+ }
+
+ public static void setSqlDateFormat(DateFormat dateFormat) {
+ JSON.sqlDateFormat = dateFormat;
+ rebuildJsonb();
+ }
+
+ {{! JSON-B has no equivalent of Gson's lenient mode or Jackson's ALLOW_* read features, so this
+ setter is not generated for JSON-B; ApiClient omits its wrapper too. }}
+
+ /**
+ * JSON-B adapter for java.io.File, the mapping of {@code format: binary}.
+ *
+ * Without it JSON-B introspects {@code File} as a bean and fails both ways: serializing
+ * recurses through {@code getAbsoluteFile()} and aborts with "Recursive reference has been
+ * found in class java.io.File", while deserializing demands {@code START_OBJECT} where the
+ * schema carries a string. The representation matches the Jackson serialization library:
+ * the absolute path on the way out, {@code new File(String)} on the way in.
+ */
+ private static class FileAdapter implements JsonbAdapter {
+
+ @Override
+ public String adaptToJson(File file) {
+ if (file == null) {
+ return null;
+ }
+ return file.getAbsolutePath();
+ }
+
+ @Override
+ public File adaptFromJson(String value) {
+ if (value == null) {
+ return null;
+ }
+ return new File(value);
+ }
+ }
+
+ /**
+ * JSON-B adapter for java.util.Date driven by a configurable DateFormat.
+ */
+ private static class DateAdapter implements JsonbAdapter {
+ private final DateFormat dateFormat;
+
+ DateAdapter(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ @Override
+ public String adaptToJson(Date date) {
+ if (date == null) {
+ return null;
+ }
+ // DateFormat is not thread-safe.
+ synchronized (dateFormat) {
+ return dateFormat.format(date);
+ }
+ }
+
+ @Override
+ public Date adaptFromJson(String value) throws ParseException {
+ if (value == null) {
+ return null;
+ }
+ synchronized (dateFormat) {
+ return dateFormat.parse(value);
+ }
+ }
+ }
+
+ /**
+ * JSON-B adapter for java.sql.Date driven by a configurable DateFormat.
+ */
+ private static class SqlDateAdapter implements JsonbAdapter {
+ private final DateFormat dateFormat;
+
+ SqlDateAdapter(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ @Override
+ public String adaptToJson(java.sql.Date date) {
+ if (date == null) {
+ return null;
+ }
+ synchronized (dateFormat) {
+ return dateFormat.format(date);
+ }
+ }
+
+ @Override
+ public java.sql.Date adaptFromJson(String value) throws ParseException {
+ if (value == null) {
+ return null;
+ }
+ synchronized (dateFormat) {
+ return new java.sql.Date(dateFormat.parse(value).getTime());
+ }
+ }
+ }
+ {{#jsr310}}
+
+ /**
+ * JSON-B adapter for java.time.OffsetDateTime driven by a configurable DateTimeFormatter.
+ */
+ private static class OffsetDateTimeAdapter implements JsonbAdapter {
+ private final DateTimeFormatter formatter;
+
+ OffsetDateTimeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ @Override
+ public String adaptToJson(OffsetDateTime value) {
+ return value == null ? null : formatter.format(value);
+ }
+
+ @Override
+ public OffsetDateTime adaptFromJson(String value) {
+ return value == null ? null : OffsetDateTime.parse(value, formatter);
+ }
+ }
+
+ /**
+ * JSON-B adapter for java.time.LocalDate driven by a configurable DateTimeFormatter.
+ */
+ private static class LocalDateAdapter implements JsonbAdapter {
+ private final DateTimeFormatter formatter;
+
+ LocalDateAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ @Override
+ public String adaptToJson(LocalDate value) {
+ return value == null ? null : formatter.format(value);
+ }
+
+ @Override
+ public LocalDate adaptFromJson(String value) {
+ return value == null ? null : LocalDate.parse(value, formatter);
+ }
+ }
+
+ /**
+ * JSON-B adapter for java.time.LocalDateTime driven by a configurable DateTimeFormatter.
+ */
+ private static class LocalDateTimeAdapter implements JsonbAdapter {
+ private final DateTimeFormatter formatter;
+
+ LocalDateTimeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ @Override
+ public String adaptToJson(LocalDateTime value) {
+ return value == null ? null : formatter.format(value);
+ }
+
+ @Override
+ public LocalDateTime adaptFromJson(String value) {
+ return value == null ? null : LocalDateTime.parse(value, formatter);
+ }
+ }
+ {{/jsr310}}
+ {{#joda}}
+
+ /**
+ * JSON-B adapter for org.joda.time.DateTime driven by a configurable DateTimeFormatter.
+ */
+ private static class JodaDateTimeAdapter implements JsonbAdapter {
+ private final DateTimeFormatter formatter;
+
+ JodaDateTimeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ @Override
+ public String adaptToJson(DateTime value) {
+ return value == null ? null : formatter.print(value);
+ }
+
+ @Override
+ public DateTime adaptFromJson(String value) {
+ return value == null ? null : formatter.parseDateTime(value);
+ }
+ }
+
+ /**
+ * JSON-B adapter for org.joda.time.LocalDate driven by a configurable DateTimeFormatter.
+ */
+ private static class JodaLocalDateAdapter implements JsonbAdapter {
+ private final DateTimeFormatter formatter;
+
+ JodaLocalDateAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ @Override
+ public String adaptToJson(LocalDate value) {
+ return value == null ? null : formatter.print(value);
+ }
+
+ @Override
+ public LocalDate adaptFromJson(String value) {
+ return value == null ? null : formatter.parseLocalDate(value);
+ }
+ }
+ {{/joda}}
+
+ public static boolean isInstanceOf(Class> type, Object instance, Set> visited) {
+ if (instance == null) {
+ // null never matches a concrete schema type; nullable composed schemas accept
+ // null in setActualInstance before consulting this method
+ return false;
+ }
+ if (type.isInstance(instance)) {
+ return true;
+ }
+ if (instance instanceof AbstractOpenApiSchema) {
+ AbstractOpenApiSchema schema = (AbstractOpenApiSchema) instance;
+ visited.add(schema.getClass());
+ for (Class> allowedType : schema.getSchemas().values()) {
+ if (visited.contains(allowedType)) {
+ continue;
+ }
+ if (isInstanceOf(type, schema.getActualInstance(), visited)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ {{/isJsonb}}
+
+ {{#jsr310}}
+ {{#isGson}}
+ public static void setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
+ offsetDateTimeTypeAdapter.setFormat(dateFormat);
+ }
+
+ public static void setLocalDateFormat(DateTimeFormatter dateFormat) {
+ localDateTypeAdapter.setFormat(dateFormat);
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ public static void setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
+ {{^useJackson3}}
+ mapper.registerModule(new SimpleModule()
+ .addSerializer(OffsetDateTime.class, new JsonSerializer() {
+ @Override
+ public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateFormat.format(value));
+ }
+ })
+ .addDeserializer(OffsetDateTime.class, new JsonDeserializer() {
+ @Override
+ public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return OffsetDateTime.parse(p.getText(), dateFormat);
+ }
+ }));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .addModule(new SimpleModule()
+ .addSerializer(OffsetDateTime.class, new ValueSerializer() {
+ @Override
+ public void serialize(OffsetDateTime value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateFormat.format(value));
+ }
+ })
+ .addDeserializer(OffsetDateTime.class, new ValueDeserializer() {
+ @Override
+ public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ return OffsetDateTime.parse(p.getString(), dateFormat);
+ }
+ }))
+ .build();
+ {{/useJackson3}}
+ }
+
+ public static void setLocalDateFormat(DateTimeFormatter dateFormat) {
+ {{^useJackson3}}
+ mapper.registerModule(new SimpleModule()
+ .addSerializer(LocalDate.class, new JsonSerializer() {
+ @Override
+ public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateFormat.format(value));
+ }
+ })
+ .addDeserializer(LocalDate.class, new JsonDeserializer() {
+ @Override
+ public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return LocalDate.parse(p.getText(), dateFormat);
+ }
+ }));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .addModule(new SimpleModule()
+ .addSerializer(LocalDate.class, new ValueSerializer() {
+ @Override
+ public void serialize(LocalDate value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateFormat.format(value));
+ }
+ })
+ .addDeserializer(LocalDate.class, new ValueDeserializer() {
+ @Override
+ public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ return LocalDate.parse(p.getString(), dateFormat);
+ }
+ }))
+ .build();
+ {{/useJackson3}}
+ }
+ {{/isJackson}}
+ {{#isJsonb}}
+ public static void setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
+ JSON.offsetDateTimeFormat = dateFormat;
+ rebuildJsonb();
+ }
+
+ public static void setLocalDateFormat(DateTimeFormatter dateFormat) {
+ JSON.localDateFormat = dateFormat;
+ rebuildJsonb();
+ }
+
+ public static void setLocalDateTimeFormat(DateTimeFormatter dateFormat) {
+ JSON.localDateTimeFormat = dateFormat;
+ rebuildJsonb();
+ }
+ {{/isJsonb}}
+ {{/jsr310}}
+
+ {{#joda}}
+ {{#isGson}}
+ public static void setDateTimeFormat(DateTimeFormatter dateFormat) {
+ dateTimeTypeAdapter.setFormat(dateFormat);
+ }
+
+ public static void setLocalDateFormat(DateTimeFormatter dateFormat) {
+ localDateTypeAdapter.setFormat(dateFormat);
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ public static void setDateTimeFormat(org.joda.time.format.DateTimeFormatter dateFormat) {
+ {{^useJackson3}}
+ mapper.registerModule(new SimpleModule()
+ .addSerializer(DateTime.class, new JsonSerializer() {
+ @Override
+ public void serialize(DateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateFormat.print(value));
+ }
+ })
+ .addDeserializer(DateTime.class, new JsonDeserializer() {
+ @Override
+ public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return dateFormat.parseDateTime(p.getText());
+ }
+ }));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .addModule(new SimpleModule()
+ .addSerializer(DateTime.class, new ValueSerializer() {
+ @Override
+ public void serialize(DateTime value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateFormat.print(value));
+ }
+ })
+ .addDeserializer(DateTime.class, new ValueDeserializer() {
+ @Override
+ public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ return dateFormat.parseDateTime(p.getString());
+ }
+ }))
+ .build();
+ {{/useJackson3}}
+ }
+
+ public static void setLocalDateFormat(org.joda.time.format.DateTimeFormatter dateFormat) {
+ {{^useJackson3}}
+ mapper.registerModule(new SimpleModule()
+ .addSerializer(org.joda.time.LocalDate.class, new JsonSerializer() {
+ @Override
+ public void serialize(org.joda.time.LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateFormat.print(value));
+ }
+ })
+ .addDeserializer(org.joda.time.LocalDate.class, new JsonDeserializer() {
+ @Override
+ public org.joda.time.LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return dateFormat.parseLocalDate(p.getText());
+ }
+ }));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ mapper = ((JsonMapper) mapper).rebuild()
+ .addModule(new SimpleModule()
+ .addSerializer(org.joda.time.LocalDate.class, new ValueSerializer() {
+ @Override
+ public void serialize(org.joda.time.LocalDate value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateFormat.print(value));
+ }
+ })
+ .addDeserializer(org.joda.time.LocalDate.class, new ValueDeserializer() {
+ @Override
+ public org.joda.time.LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ return dateFormat.parseLocalDate(p.getString());
+ }
+ }))
+ .build();
+ {{/useJackson3}}
+ }
+ {{/isJackson}}
+ {{#isJsonb}}
+ public static void setDateTimeFormat(DateTimeFormatter dateFormat) {
+ JSON.dateTimeFormat = dateFormat;
+ rebuildJsonb();
+ }
+
+ public static void setLocalDateFormat(DateTimeFormatter dateFormat) {
+ JSON.jodaLocalDateFormat = dateFormat;
+ rebuildJsonb();
+ }
+ {{/isJsonb}}
+ {{/joda}}
+
+ /**
+ * Serialize the given Java object into JSON string.
+ *
+ * @param obj Object
+ * @return String representation of the JSON
+ */
+ public String serialize(Object obj) {
+ {{#isGson}}
+ return gson.toJson(obj);
+ {{/isGson}}
+ {{#isJackson}}
+ try {
+ return mapper.writeValueAsString(obj);
+ } catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}IOException{{/useJackson3}} e) {
+ throw new RuntimeException(e);
+ }
+ {{/isJackson}}
+ {{#isJsonb}}
+ return jsonb.toJson(obj);
+ {{/isJsonb}}
+ }
+
+ /**
+ * Deserialize the given JSON string to Java object.
+ *
+ * @param Type
+ * @param body The JSON string
+ * @param returnType The type to deserialize into
+ * @return The deserialized Java object
+ */
+ @SuppressWarnings("unchecked")
+ public T deserialize(String body, Type returnType) {
+ {{#isGson}}
+ try {
+ if (isLenientOnJson) {
+ JsonReader jsonReader = new JsonReader(new StringReader(body));
+ jsonReader.setLenient(true);
+ return gson.fromJson(jsonReader, returnType);
+ } else {
+ return gson.fromJson(body, returnType);
+ }
+ } catch (JsonParseException e) {
+ if (returnType.equals(String.class)) {
+ return (T) body;
+ } else {
+ throw (e);
+ }
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ try {
+ JavaType type = mapper.getTypeFactory().constructType(returnType);
+ return mapper.readValue(body, type);
+ } catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}IOException{{/useJackson3}} e) {
+ if (returnType.equals(String.class)) {
+ return (T) body;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ {{/isJackson}}
+ {{#isJsonb}}
+ return jsonb.fromJson(body, returnType);
+ {{/isJsonb}}
+ }
+
+ /**
+ * Deserialize the given JSON InputStream to a Java object.
+ *
+ * @param Type
+ * @param inputStream The JSON InputStream
+ * @param returnType The type to deserialize into
+ * @return The deserialized Java object
+ */
+ @SuppressWarnings("unchecked")
+ public T deserialize(InputStream inputStream, Type returnType) throws IOException {
+ {{#isGson}}
+ try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
+ if (isLenientOnJson) {
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+ return gson.fromJson(jsonReader, returnType);
+ } else {
+ return gson.fromJson(reader, returnType);
+ }
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ JavaType type = mapper.getTypeFactory().constructType(returnType);
+ return mapper.readValue(inputStream, type);
+ {{/isJackson}}
+ {{#isJsonb}}
+ return jsonb.fromJson(inputStream, returnType);
+ {{/isJsonb}}
+ }
+
+ {{#isGson}}
+ /**
+ * Gson TypeAdapter for Byte Array type
+ */
+ public static class ByteArrayAdapter extends TypeAdapter {
+
+ @Override
+ public void write(JsonWriter out, byte[] value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ } else {
+ out.value(ByteString.of(value).base64());
+ }
+ }
+
+ @Override
+ public byte[] read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String bytesAsBase64 = in.nextString();
+ ByteString byteString = ByteString.decodeBase64(bytesAsBase64);
+ return byteString.toByteArray();
+ }
+ }
+ }
+ {{#joda}}
+ /**
+ * Gson TypeAdapter for Joda DateTime type
+ */
+ public static class DateTimeTypeAdapter extends TypeAdapter {
+
+ private DateTimeFormatter formatter;
+
+ public DateTimeTypeAdapter() {
+ this(new DateTimeFormatterBuilder()
+ .append(ISODateTimeFormat.dateTime().getPrinter(), ISODateTimeFormat.dateOptionalTimeParser().getParser())
+ .toFormatter());
+ }
+
+ public DateTimeTypeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ public void setFormat(DateTimeFormatter dateFormat) {
+ this.formatter = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, DateTime date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ out.value(formatter.print(date));
+ }
+ }
+
+ @Override
+ public DateTime read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ return formatter.parseDateTime(date);
+ }
+ }
+ }
+
+ /**
+ * Gson TypeAdapter for Joda LocalDate type
+ */
+ public static class LocalDateTypeAdapter extends TypeAdapter {
+
+ private DateTimeFormatter formatter;
+
+ public LocalDateTypeAdapter() {
+ this(ISODateTimeFormat.date());
+ }
+
+ public LocalDateTypeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ public void setFormat(DateTimeFormatter dateFormat) {
+ this.formatter = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, LocalDate date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ out.value(formatter.print(date));
+ }
+ }
+
+ @Override
+ public LocalDate read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ return formatter.parseLocalDate(date);
+ }
+ }
+ }
+
+ {{/joda}}
+ {{#jsr310}}
+ /**
+ * Gson TypeAdapter for JSR310 OffsetDateTime type
+ */
+ public static class OffsetDateTimeTypeAdapter extends TypeAdapter {
+
+ private DateTimeFormatter formatter;
+
+ public OffsetDateTimeTypeAdapter() {
+ this(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+ }
+
+ public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ public void setFormat(DateTimeFormatter dateFormat) {
+ this.formatter = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, OffsetDateTime date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ out.value(formatter.format(date));
+ }
+ }
+
+ @Override
+ public OffsetDateTime read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ if (date.endsWith("+0000")) {
+ date = date.substring(0, date.length()-5) + "Z";
+ }
+ return OffsetDateTime.parse(date, formatter);
+ }
+ }
+ }
+
+ /**
+ * Gson TypeAdapter for JSR310 LocalDate type
+ */
+ public static class LocalDateTypeAdapter extends TypeAdapter {
+
+ private DateTimeFormatter formatter;
+
+ public LocalDateTypeAdapter() {
+ this(DateTimeFormatter.ISO_LOCAL_DATE);
+ }
+
+ public LocalDateTypeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ public void setFormat(DateTimeFormatter dateFormat) {
+ this.formatter = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, LocalDate date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ out.value(formatter.format(date));
+ }
+ }
+
+ @Override
+ public LocalDate read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ return LocalDate.parse(date, formatter);
+ }
+ }
+ }
+
+ /**
+ * Gson TypeAdapter for JSR310 LocalDateTime type
+ */
+ public static class LocalDateTimeTypeAdapter extends TypeAdapter {
+
+ private DateTimeFormatter formatter;
+
+ public LocalDateTimeTypeAdapter() {
+ this(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ }
+
+ public LocalDateTimeTypeAdapter(DateTimeFormatter formatter) {
+ this.formatter = formatter;
+ }
+
+ public void setFormat(DateTimeFormatter dateFormat) {
+ this.formatter = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, LocalDateTime date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ out.value(formatter.format(date));
+ }
+ }
+
+ @Override
+ public LocalDateTime read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ try {
+ return LocalDateTime.parse(date, formatter);
+ } catch (DateTimeParseException e) {
+ if (date.length() > 10 && date.charAt(10) == ' ') {
+ date = date.substring(0, 10) + 'T' + date.substring(11);
+ return LocalDateTime.parse(date, formatter);
+ }
+ throw e;
+ }
+ }
+ }
+ }
+
+ public static void setLocalDateTimeFormat(DateTimeFormatter dateFormat) {
+ localDateTimeTypeAdapter.setFormat(dateFormat);
+ }
+
+ {{/jsr310}}
+ /**
+ * Gson TypeAdapter for java.sql.Date type
+ * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used
+ * (more efficient than SimpleDateFormat).
+ */
+ public static class SqlDateTypeAdapter extends TypeAdapter {
+
+ private DateFormat dateFormat;
+
+ public SqlDateTypeAdapter() {}
+
+ public SqlDateTypeAdapter(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ public void setFormat(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, java.sql.Date date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ String value;
+ if (dateFormat != null) {
+ value = dateFormat.format(date);
+ } else {
+ value = date.toString();
+ }
+ out.value(value);
+ }
+ }
+
+ @Override
+ public java.sql.Date read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ try {
+ if (dateFormat != null) {
+ return new java.sql.Date(dateFormat.parse(date).getTime());
+ }
+ return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime());
+ } catch (ParseException e) {
+ throw new JsonParseException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gson TypeAdapter for java.util.Date type
+ * If the dateFormat is null, ISO8601Utils will be used.
+ */
+ public static class DateTypeAdapter extends TypeAdapter {
+
+ private DateFormat dateFormat;
+
+ public DateTypeAdapter() {}
+
+ public DateTypeAdapter(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ public void setFormat(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ @Override
+ public void write(JsonWriter out, Date date) throws IOException {
+ if (date == null) {
+ out.nullValue();
+ } else {
+ String value;
+ if (dateFormat != null) {
+ value = dateFormat.format(date);
+ } else {
+ value = ISO8601Utils.format(date, true);
+ }
+ out.value(value);
+ }
+ }
+
+ @Override
+ public Date read(JsonReader in) throws IOException {
+ try {
+ switch (in.peek()) {
+ case NULL:
+ in.nextNull();
+ return null;
+ default:
+ String date = in.nextString();
+ try {
+ if (dateFormat != null) {
+ return dateFormat.parse(date);
+ }
+ return ISO8601Utils.parse(date, new ParsePosition(0));
+ } catch (ParseException e) {
+ throw new JsonParseException(e);
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ throw new JsonParseException(e);
+ }
+ }
+ }
+ {{/isGson}}
+{{#legacyDates}}
+
+ {{! `dateLibrary=legacy` maps BOTH `format: date` and `format: date-time` onto
+ java.util.Date, so a single type-keyed (de)serializer cannot serve both. The
+ date-time form (a full instant, rendered in UTC) was therefore applied to
+ date-only properties too, which appends a time component the server rejects AND
+ shifts the calendar day backwards on any JVM at a positive UTC offset. The
+ adapter below is attached per-property from pojo.mustache, for `isDate` only, so
+ the public API stays java.util.Date. Jackson needs no adapter - it expresses the
+ same thing with a per-property @JsonFormat pattern. }}
+ {{! Deliberately left on the DEFAULT time zone. A `format: date` value carries no zone, so
+ the only sane contract is that the calendar day written out is the day the caller set:
+ pinning UTC re-introduces the very off-by-one-day shift this fixes, because a Date at
+ local midnight on the 15th is the 14th at 23:00Z. Both directions use this same format,
+ so the value round-trips exactly. }}
+ private static java.text.DateFormat dateOnlyFormat() {
+ return new java.text.SimpleDateFormat("yyyy-MM-dd");
+ }
+{{#isJackson}}
+
+ {{! A per-property @JsonFormat(pattern="yyyy-MM-dd") would look simpler but is WRONG here:
+ with no explicit `timezone` it resolves against the MAPPER's default zone, and Jackson
+ defaults that to UTC rather than the JVM zone - so a Date at local midnight on the 15th
+ still serialised as "2020-01-14", and Jackson would disagree with the Gson and JSON-B
+ paths. Going through dateOnlyFormat() keeps all four serializers byte-identical on the
+ wire. }}
+ {{^useJackson3}}
+ public static class DateOnlySerializer extends JsonSerializer {
+ @Override
+ public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(dateOnlyFormat().format(value));
+ }
+ }
+
+ public static class DateOnlyDeserializer extends JsonDeserializer {
+ @Override
+ public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ try {
+ return dateOnlyFormat().parse(p.getText());
+ } catch (ParseException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ {{/useJackson3}}
+ {{#useJackson3}}
+ public static class DateOnlySerializer extends ValueSerializer {
+ @Override
+ public void serialize(Date value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
+ gen.writeString(dateOnlyFormat().format(value));
+ }
+ }
+
+ public static class DateOnlyDeserializer extends ValueDeserializer {
+ @Override
+ public Date deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException {
+ try {
+ return dateOnlyFormat().parse(p.getString());
+ } catch (ParseException e) {
+ throw new IllegalArgumentException("Failed to parse a date-only java.util.Date", e);
+ }
+ }
+ }
+ {{/useJackson3}}
+{{/isJackson}}
+{{#isGson}}
+
+ /**
+ * Gson type adapter for a date-only java.util.Date (OpenAPI {@code format: date}).
+ */
+ public static class DateOnlyTypeAdapter extends TypeAdapter {
+ @Override
+ public void write(JsonWriter out, Date value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ return;
+ }
+ out.value(dateOnlyFormat().format(value));
+ }
+
+ @Override
+ public Date read(JsonReader in) throws IOException {
+ {{! Fully qualified: the Gson import block does not pull in JsonToken (the unqualified
+ JsonToken in this file is Jackson's, imported only for the Jackson serializer). }}
+ if (in.peek() == com.google.gson.stream.JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ }
+ String date = in.nextString();
+ try {
+ return dateOnlyFormat().parse(date);
+ } catch (ParseException e) {
+ throw new JsonParseException(e);
+ }
+ }
+ }
+{{/isGson}}
+{{#isJsonb}}
+
+ /**
+ * JSON-B adapter for a date-only java.util.Date (OpenAPI {@code format: date}).
+ */
+ public static class DateOnlyJsonbAdapter implements JsonbAdapter {
+ @Override
+ public String adaptToJson(Date value) {
+ return value == null ? null : dateOnlyFormat().format(value);
+ }
+
+ @Override
+ public Date adaptFromJson(String value) throws ParseException {
+ return value == null ? null : dateOnlyFormat().parse(value);
+ }
+ }
+
+ {{! The generated CustomJsonbDeserializer binds each property with
+ jsonb.fromJson(value, fieldType(name)) - i.e. by DECLARED TYPE - so a field-level
+ @JsonbTypeAdapter is never consulted on that path and Yasson falls back to its own
+ java.util.Date parsing, which rejects a bare "yyyy-MM-dd". Deserialization of a
+ date-only property therefore has to go through this helper explicitly. }}
+ public static Date dateOnlyFromJson(jakarta.json.JsonValue value) {
+ try {
+ return dateOnlyFormat().parse(((jakarta.json.JsonString) value).getString());
+ } catch (ParseException e) {
+ throw new jakarta.json.bind.JsonbException("Failed to parse a date-only java.util.Date: " + value, e);
+ }
+ }
+{{/isJsonb}}
+{{/legacyDates}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressRequestBody.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressRequestBody.mustache
new file mode 100644
index 000000000000..efae5d993e7b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressRequestBody.mustache
@@ -0,0 +1,103 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import okio.Buffer;
+import okio.BufferedSink;
+import okio.ForwardingSink;
+import okio.Okio;
+import okio.Sink;
+
+public class ProgressRequestBody extends RequestBody {
+
+ private final RequestBody requestBody;
+
+ private final ApiCallback callback;
+
+ public ProgressRequestBody(RequestBody requestBody, ApiCallback callback) {
+ this.requestBody = requestBody;
+ this.callback = Objects.requireNonNull(callback);
+ }
+
+ /**
+ * The body this one wraps. Package-private so that {@link GzipRequestInterceptor} can compress
+ * that body directly: draining this wrapper to compress it would report the upload as finished
+ * before the request reached the network.
+ *
+ * @return the wrapped request body
+ */
+ RequestBody getDelegate() {
+ return requestBody;
+ }
+
+ /**
+ * The callback this body reports upload progress to. Package-private so that
+ * {@link GzipRequestInterceptor} can re-wrap the compressed body with the same callback.
+ *
+ * @return the progress callback
+ */
+ ApiCallback getCallback() {
+ return callback;
+ }
+
+ @Override
+ public MediaType contentType() {
+ return requestBody.contentType();
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return requestBody.contentLength();
+ }
+
+ @Override
+ public boolean isDuplex() {
+ return requestBody.isDuplex();
+ }
+
+ @Override
+ public boolean isOneShot() {
+ return requestBody.isOneShot();
+ }
+
+ @Override
+ public void writeTo(BufferedSink sink) throws IOException {
+ CountingSink countingSink = new CountingSink(sink);
+ BufferedSink bufferedSink = Okio.buffer(countingSink);
+ requestBody.writeTo(bufferedSink);
+ bufferedSink.flush();
+
+ long bytesWritten = countingSink.bytesWritten;
+ long contentLength = contentLength();
+ callback.onUploadProgress(bytesWritten, contentLength, true);
+ }
+
+ private final class CountingSink extends ForwardingSink {
+
+ private long bytesWritten = 0L;
+ private long contentLength = 0L;
+
+ public CountingSink(Sink delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public void write(Buffer source, long byteCount) throws IOException {
+ super.write(source, byteCount);
+ if (contentLength == 0) {
+ contentLength = contentLength();
+ }
+
+ bytesWritten += byteCount;
+ // writeTo emits the single terminal callback once the body is fully written; reporting
+ // done here as well would fire it twice for a known-length body.
+ callback.onUploadProgress(bytesWritten, contentLength, false);
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressResponseBody.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressResponseBody.mustache
new file mode 100644
index 000000000000..eaffa96d327b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/ProgressResponseBody.mustache
@@ -0,0 +1,61 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import okhttp3.MediaType;
+import okhttp3.ResponseBody;
+
+import java.io.IOException;
+
+import okio.Buffer;
+import okio.BufferedSource;
+import okio.ForwardingSource;
+import okio.Okio;
+import okio.Source;
+
+public class ProgressResponseBody extends ResponseBody {
+
+ private final ResponseBody responseBody;
+ private final ApiCallback callback;
+ private BufferedSource bufferedSource;
+
+ public ProgressResponseBody(ResponseBody responseBody, ApiCallback callback) {
+ this.responseBody = responseBody;
+ this.callback = callback;
+ }
+
+ @Override
+ public MediaType contentType() {
+ return responseBody.contentType();
+ }
+
+ @Override
+ public long contentLength() {
+ return responseBody.contentLength();
+ }
+
+ @Override
+ public BufferedSource source() {
+ // Memoized: OkHttp calls source() again on close(), and a second ForwardingSource would
+ // reset the progress counter and re-wrap an already partially consumed stream.
+ if (bufferedSource == null) {
+ bufferedSource = Okio.buffer(source(responseBody.source()));
+ }
+ return bufferedSource;
+ }
+
+ private Source source(Source source) {
+ return new ForwardingSource(source) {
+ long totalBytesRead = 0L;
+
+ @Override
+ public long read(Buffer sink, long byteCount) throws IOException {
+ long bytesRead = super.read(sink, byteCount);
+ // read() returns the number of bytes read, or -1 if this source is exhausted.
+ totalBytesRead += bytesRead != -1 ? bytesRead : 0;
+ callback.onDownloadProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
+ return bytesRead;
+ }
+ };
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/README.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/README.mustache
new file mode 100644
index 000000000000..de3afa6c1875
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/README.mustache
@@ -0,0 +1,195 @@
+# {{artifactId}}
+
+{{appName}}
+- API version: {{appVersion}}
+{{^hideGenerationTimestamp}}
+ - Build date: {{generatedDate}}
+{{/hideGenerationTimestamp}}
+ - Generator version: {{generatorVersion}}
+
+{{{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 (3.8.3+)/Gradle (7.2+)
+
+## 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
+ repositories {
+ mavenCentral() // Needed if the '{{{artifactId}}}' jar has been published to maven central.
+ mavenLocal() // Needed if the '{{{artifactId}}}' jar has been published to the local maven repo.
+ }
+
+ dependencies {
+ implementation "{{{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 classes:
+import {{{invokerPackage}}}.ApiClient;
+import {{{invokerPackage}}}.ApiException;
+import {{{invokerPackage}}}.Configuration;{{#hasAuthMethods}}
+import {{{invokerPackage}}}.auth.*;{{/hasAuthMethods}}
+import {{{modelPackage}}}.*;
+import {{{package}}}.{{{classname}}};
+
+public class Example {
+ public static void main(String[] args) {
+ ApiClient defaultClient = Configuration.getDefaultApiClient();
+ defaultClient.setBasePath("{{{basePath}}}");
+ {{#withAWSV4Signature}}
+ // Configure AWS Signature V4 authorization
+ defaultClient.setAWS4Configuration("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY", "REGION", "SERVICE")
+ {{/withAWSV4Signature}}
+ {{#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}}
+ {{/authMethods}}
+ {{/hasAuthMethods}}
+
+ {{{classname}}} apiInstance = new {{{classname}}}(defaultClient);
+ {{#allParams}}
+ {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}}
+ {{/allParams}}
+ try {
+ {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}{{^vendorExtensions.x-group-parameters}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}
+ .{{{paramName}}}({{{paramName}}}){{/optionalParams}}
+ .execute();{{/vendorExtensions.x-group-parameters}}{{#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}}** {{path}} | {{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/okhttp/additional_properties.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/additional_properties.mustache
new file mode 100644
index 000000000000..f3fe50b5d601
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/additional_properties.mustache
@@ -0,0 +1,58 @@
+{{#isAdditionalPropertiesTrue}}
+ /**
+ * A container for additional, undeclared properties.
+ * This is a holder for any undeclared properties as specified with
+ * the 'additionalProperties' keyword in the OAS document.
+ */
+ {{#isJackson}}
+ @JsonIgnore
+ {{/isJackson}}
+ {{#isJsonb}}
+ @JsonbTransient
+ {{/isJsonb}}
+ private Map additionalProperties;
+
+ /**
+ * Set the additional (undeclared) property with the specified name and value.
+ * If the property does not already exist, create it otherwise replace it.
+ *
+ * @param key name of the property
+ * @param value value of the property
+ * @return the {{classname}} instance itself
+ */
+ {{#isJackson}}
+ @JsonAnySetter
+ {{/isJackson}}
+ public {{classname}} putAdditionalProperty(String key, Object value) {
+ if (this.additionalProperties == null) {
+ this.additionalProperties = new HashMap();
+ }
+ this.additionalProperties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Return the additional (undeclared) property.
+ *
+ * @return a map of objects
+ */
+ {{#isJackson}}
+ @JsonAnyGetter
+ {{/isJackson}}
+ public Map getAdditionalProperties() {
+ return additionalProperties;
+ }
+
+ /**
+ * Return the additional (undeclared) property with the specified name.
+ *
+ * @param key name of the property
+ * @return an object
+ */
+ public Object getAdditionalProperty(String key) {
+ if (this.additionalProperties == null) {
+ return null;
+ }
+ return this.additionalProperties.get(key);
+ }
+{{/isAdditionalPropertiesTrue}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/anyof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/anyof_model.mustache
new file mode 100644
index 000000000000..9e1847fdd3fb
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/anyof_model.mustache
@@ -0,0 +1,1260 @@
+{{#isGson}}
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.JsonPrimitive;
+{{! TypeAdapter, JsonAdapter, SerializedName, JsonReader and JsonWriter are contributed via
+ model.imports by JavaClientCodegen; emitting them here as well produced duplicates. }}
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonArray;
+{{/isGson}}
+{{#isJackson}}
+{{#isAdditionalPropertiesTrue}}
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+{{/isAdditionalPropertiesTrue}}
+import {{jacksonPackage}}.core.JsonGenerator;
+import {{jacksonPackage}}.core.JsonParser;
+import {{jacksonPackage}}.core.JsonToken;
+import {{jacksonPackage}}.core.type.TypeReference;
+import {{jacksonPackage}}.databind.DeserializationContext;
+{{^useJackson3}}
+import {{jacksonPackage}}.databind.JsonMappingException;
+{{/useJackson3}}
+import {{jacksonPackage}}.databind.JsonNode;
+import {{jacksonPackage}}.databind.MapperFeature;
+{{^useJackson3}}
+import {{jacksonPackage}}.databind.SerializerProvider;
+{{/useJackson3}}
+import {{jacksonPackage}}.databind.annotation.JsonDeserialize;
+import {{jacksonPackage}}.databind.annotation.JsonSerialize;
+import {{jacksonPackage}}.databind.deser.std.StdDeserializer;
+import {{jacksonPackage}}.databind.ser.std.StdSerializer;
+import {{jacksonPackage}}.databind.JavaType;
+{{#useJackson3}}
+import {{jacksonPackage}}.core.JacksonException;
+import {{jacksonPackage}}.databind.DatabindException;
+import {{jacksonPackage}}.databind.SerializationContext;
+{{/useJackson3}}
+{{/isJackson}}
+
+{{#isJsonb}}
+{{! additional_properties.mustache annotates its holder field with @JsonbTransient, so the
+ import has to be here too - a pojo gets it from pojo.mustache, a composed model does not. }}
+import jakarta.json.bind.annotation.JsonbTransient;
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+{{/isJsonb}}
+{{! java.io.IOException, java.util.ArrayList, java.util.HashMap, java.util.List and
+ java.util.Map are contributed via model.imports by JavaClientCodegen.fromModel, which is
+ also where codegen imports them for container properties. Emitting them here as well
+ produced duplicate import lines. The rest have no Java importMapping entry and are not
+ duplicated, so they stay. }}
+import java.lang.reflect.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.Collections;
+import java.util.HashSet;
+{{! java.util.Objects is already imported by model.mustache for every model it renders. }}
+import java.util.StringJoiner;
+
+import {{invokerPackage}}.JSON;
+
+{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}}
+{{#isJackson}}
+@JsonDeserialize(using = {{classname}}.{{classname}}Deserializer.class)
+@JsonSerialize(using = {{classname}}.{{classname}}Serializer.class)
+{{/isJackson}}
+{{#isJsonb}}
+@JsonbTypeDeserializer({{classname}}.{{classname}}Deserializer.class)
+@JsonbTypeSerializer({{classname}}.{{classname}}Serializer.class)
+{{/isJsonb}}
+public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} {
+ private static final Logger log = Logger.getLogger({{classname}}.class.getName());
+
+{{>additional_properties}}
+ {{#isAdditionalPropertiesTrue}}
+ {{#isGson}}
+ /**
+ * Record the properties that the matched anyOf schema did not consume.
+ *
+ * The wrapper only owns what the selected child schema left behind. When that child accepts
+ * additional properties itself it absorbs them all and this records nothing, so a property is
+ * never stored — and therefore never written — twice.
+ */
+ private static void collectUnconsumedProperties({{classname}} instance, JsonElement jsonElement, Gson gson) {
+ if (jsonElement == null || !jsonElement.isJsonObject() || instance.getActualInstance() == null) {
+ return;
+ }
+ JsonElement consumedElement = gson.toJsonTree(instance.getActualInstance());
+ if (consumedElement == null || !consumedElement.isJsonObject()) {
+ return;
+ }
+ JsonObject consumed = consumedElement.getAsJsonObject();
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ if (consumed.has(entry.getKey())) {
+ continue;
+ }
+ JsonElement value = entry.getValue();
+ if (value.isJsonPrimitive()) {
+ JsonPrimitive primitive = value.getAsJsonPrimitive();
+ if (primitive.isString()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsString());
+ } else if (primitive.isNumber()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsNumber());
+ } else if (primitive.isBoolean()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsBoolean());
+ }
+ } else if (value.isJsonArray()) {
+ instance.putAdditionalProperty(entry.getKey(), gson.fromJson(value, List.class));
+ } else if (value.isJsonObject()) {
+ instance.putAdditionalProperty(entry.getKey(), gson.fromJson(value, HashMap.class));
+ }
+ }
+ }
+
+ /**
+ * Merge the unconsumed properties back into the JSON produced by the matched anyOf schema.
+ */
+ private static void writeUnconsumedProperties({{classname}} value, JsonObject obj, Gson gson) {
+ if (value.getAdditionalProperties() == null) {
+ return;
+ }
+ for (Map.Entry entry : value.getAdditionalProperties().entrySet()) {
+ if (obj.has(entry.getKey())) {
+ // the child schema owns this property
+ continue;
+ }
+ obj.add(entry.getKey(), gson.toJsonTree(entry.getValue()));
+ }
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ /**
+ * Record the properties that the matched anyOf schema did not consume.
+ *
+ * The wrapper only owns what the selected child schema left behind. When that child accepts
+ * additional properties itself it absorbs them all and this records nothing, so a property is
+ * never stored — and therefore never written — twice.
+ */
+ private static void collectUnconsumedProperties({{classname}} instance, JsonNode tree) {
+ if (tree == null || !tree.isObject() || instance.getActualInstance() == null) {
+ return;
+ }
+ JsonNode consumed = JSON.getMapper().valueToTree(instance.getActualInstance());
+ if (consumed == null || !consumed.isObject()) {
+ return;
+ }
+ java.util.Iterator> fields = tree.{{^useJackson3}}fields{{/useJackson3}}{{#useJackson3}}properties{{/useJackson3}}(){{#useJackson3}}.iterator(){{/useJackson3}};
+ while (fields.hasNext()) {
+ Map.Entry entry = fields.next();
+ if (consumed.has(entry.getKey())) {
+ continue;
+ }
+ instance.putAdditionalProperty(entry.getKey(),
+ JSON.getMapper().convertValue(entry.getValue(), Object.class));
+ }
+ }
+ {{/isJackson}}
+ {{/isAdditionalPropertiesTrue}}
+
+ {{#isGson}}
+ /**
+ * Validates the JSON Element and throws an exception if issues found
+ *
+ * @param jsonElement JSON Element
+ * @throws IOException if the JSON Element is invalid with respect to {{classname}}
+ */
+ public static void validateJsonElement(JsonElement jsonElement) throws IOException {
+ List errorMessages = new ArrayList<>();
+ int matches = 0;
+
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ try {
+ // validate the JSON object to see if any exception is thrown
+ {{#isModel}}
+ {{#hasChildren}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/hasChildren}}
+ {{^hasChildren}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isMap}}
+ {{^isMap}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/isMap}}
+ {{/isArray}}
+ {{/hasChildren}}
+ {{/isModel}}
+ {{^isModel}}
+ {{#isNumber}}
+ if (!jsonElement.getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ {{#isPrimitiveType}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{#items}}
+ {{^isAnyType}}
+ for (JsonElement element : jsonElement.getAsJsonArray()) {
+ {{#isNumber}}
+ if (!element.getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be of type Number in the JSON string but got `%s`", element.toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", element.toString()));
+ }
+ {{/isNumber}}
+ }
+ {{/isAnyType}}
+ {{/items}}
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{#items}}
+ {{^isAnyType}}
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ {{#isNumber}}
+ if (!entry.getValue().getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be of type Number in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ if (!entry.getValue().getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/isNumber}}
+ }
+ {{/isAnyType}}
+ {{/items}}
+ {{/isMap}}
+ {{^isMap}}
+ {{^isAnyType}}
+ if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isAnyType}}
+ {{/isMap}}
+ {{/isArray}}
+ {{/isPrimitiveType}}
+ {{^isPrimitiveType}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ for (JsonElement element : jsonElement.getAsJsonArray()) {
+ {{#items.isModel}}
+ {{{items.dataType}}}.validateJsonElement(element);
+ {{/items.isModel}}
+ {{^items.isModel}}
+ {{#items.isUuid}}
+ UUID.fromString(element.getAsString());
+ {{/items.isUuid}}
+ {{^items.isUuid}}
+ if (!element.isJsonPrimitive()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be a primitive type in the JSON string but got `%s`", element.toString()));
+ }
+ {{/items.isUuid}}
+ {{/items.isModel}}
+ }
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ {{#items.isModel}}
+ {{{items.dataType}}}.validateJsonElement(entry.getValue());
+ {{/items.isModel}}
+ {{^items.isModel}}
+ {{#items.isUuid}}
+ UUID.fromString(entry.getValue().getAsString());
+ {{/items.isUuid}}
+ {{^items.isUuid}}
+ if (!entry.getValue().isJsonPrimitive()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be a primitive type in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/items.isUuid}}
+ {{/items.isModel}}
+ }
+ {{/isMap}}
+ {{^isMap}}
+ {{#isUuid}}
+ UUID.fromString(jsonElement.getAsString());
+ {{/isUuid}}
+ {{^isUuid}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/isUuid}}
+ {{/isMap}}
+ {{/isArray}}
+ {{/isPrimitiveType}}
+ {{/isNumber}}
+ {{/isModel}}
+ matches++;
+ } catch (Exception e) {
+ // deserialization failed, continue
+ errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage()));
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/anyOf}}
+ {{/composedSchemas}}
+
+ if (matches == 0) {
+ throw new IOException(String.format("Failed deserialization for {{classname}}: no match found. %s", errorMessages));
+ }
+ }
+
+ public static class CustomTypeAdapterFactory implements TypeAdapterFactory {
+ @SuppressWarnings("unchecked")
+ @Override
+ public TypeAdapter create(Gson gson, TypeToken type) {
+ if (!{{classname}}.class.isAssignableFrom(type.getRawType())) {
+ return null; // this class only serializes '{{classname}}' and its subtypes
+ }
+ final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class);
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{^isArray}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}.class));
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/isArray}}
+ {{#isArray}}
+ final Type typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = new TypeToken<{{{dataType}}}>(){}.getType();
+ final TypeAdapter<{{{dataType}}}> adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}} = (TypeAdapter<{{{dataType}}}>) gson.getDelegateAdapter(this, TypeToken.get(typeInstance{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}));
+ {{/isArray}}
+ {{/anyOf}}
+ {{/composedSchemas}}
+
+ return (TypeAdapter) new TypeAdapter<{{classname}}>() {
+ @Override
+ public void write(JsonWriter out, {{classname}} value) throws IOException {
+ if (value == null || value.getActualInstance() == null) {
+ elementAdapter.write(out, null);
+ return;
+ }
+
+ JsonElement element = null;
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ // check if the actual instance is of the type `{{{dataType}}}`
+ if (value.getActualInstance() instanceof {{#isArray}}List>{{/isArray}}{{#isMap}}Map, ?>{{/isMap}}{{^isMap}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isMap}}) {
+ element = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance());
+ }
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/anyOf}}
+ {{/composedSchemas}}
+
+ if (element == null) {
+ throw new IOException("Failed to serialize as the type doesn't match anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
+ }
+
+ {{#isAdditionalPropertiesTrue}}
+ if (element.isJsonObject()) {
+ writeUnconsumedProperties(value, element.getAsJsonObject(), gson);
+ }
+ {{/isAdditionalPropertiesTrue}}
+ elementAdapter.write(out, element);
+ }
+
+ @Override
+ public {{classname}} read(JsonReader in) throws IOException {
+ Object deserialized = null;
+ JsonElement jsonElement = elementAdapter.read(in);
+
+ {{#useOneOfDiscriminatorLookup}}
+ {{#discriminator}}
+ // non-object payloads can still match a non-object anyOf schema below
+ if (jsonElement.isJsonObject()) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ JsonElement discriminatorElement = jsonObject.get("{{{propertyBaseName}}}");
+
+ // use discriminator value for faster anyOf lookup
+ {{classname}} new{{classname}} = new {{classname}}();
+ if (discriminatorElement == null || !discriminatorElement.isJsonPrimitive()) {
+ log.log(Level.WARNING, "Failed to lookup discriminator value for {{classname}} as `{{{propertyBaseName}}}` is missing or is not a primitive type in the payload.");
+ } else {
+ // look up the discriminator value in the field `{{{propertyBaseName}}}`
+ switch (discriminatorElement.getAsString()) {
+ {{#mappedModels}}
+ case "{{{mappingName}}}":
+ deserialized = adapter{{#sanitizeDataType}}{{{modelName}}}{{/sanitizeDataType}}.fromJsonTree(jsonObject);
+ new{{classname}}.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ collectUnconsumedProperties(new{{classname}}, jsonElement, gson);
+ {{/isAdditionalPropertiesTrue}}
+ return new{{classname}};
+ {{/mappedModels}}
+ default:
+ log.log(Level.WARNING, String.format(java.util.Locale.ROOT, "Failed to lookup discriminator value `%s` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", discriminatorElement.getAsString()));
+ }
+ }
+ }
+
+ {{/discriminator}}
+ {{/useOneOfDiscriminatorLookup}}
+ ArrayList errorMessages = new ArrayList<>();
+ TypeAdapter actualAdapter = elementAdapter;
+
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ // deserialize {{{dataType}}}
+ try {
+ // validate the JSON object to see if any exception is thrown
+ {{#isModel}}
+ {{#hasChildren}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/hasChildren}}
+ {{^hasChildren}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isMap}}
+ {{^isMap}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/isMap}}
+ {{/isArray}}
+ {{/hasChildren}}
+ {{/isModel}}
+ {{^isModel}}
+ {{#isNumber}}
+ if (!jsonElement.getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ {{#isPrimitiveType}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{#items}}
+ {{^isAnyType}}
+ for (JsonElement element : jsonElement.getAsJsonArray()) {
+ {{#isNumber}}
+ if (!element.getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be of type Number in the JSON string but got `%s`", element.toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ if (!element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", element.toString()));
+ }
+ {{/isNumber}}
+ }
+ {{/isAnyType}}
+ {{/items}}
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{#items}}
+ {{^isAnyType}}
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ {{#isNumber}}
+ if (!entry.getValue().getAsJsonPrimitive().isNumber()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be of type Number in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/isNumber}}
+ {{^isNumber}}
+ if (!entry.getValue().getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/isNumber}}
+ }
+ {{/isAnyType}}
+ {{/items}}
+ {{/isMap}}
+ {{^isMap}}
+ {{^isAnyType}}
+ if (!jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ {{/isAnyType}}
+ {{/isMap}}
+ {{/isArray}}
+ {{/isPrimitiveType}}
+ {{^isPrimitiveType}}
+ {{#isArray}}
+ if (!jsonElement.isJsonArray()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ for (JsonElement element : jsonElement.getAsJsonArray()) {
+ {{#items.isModel}}
+ {{{items.dataType}}}.validateJsonElement(element);
+ {{/items.isModel}}
+ {{^items.isModel}}
+ {{#items.isUuid}}
+ UUID.fromString(element.getAsString());
+ {{/items.isUuid}}
+ {{^items.isUuid}}
+ if (!element.isJsonPrimitive()) {
+ throw new IllegalArgumentException(String.format("Expected array items to be a primitive type in the JSON string but got `%s`", element.toString()));
+ }
+ {{/items.isUuid}}
+ {{/items.isModel}}
+ }
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ if (!jsonElement.isJsonObject()) {
+ throw new IllegalArgumentException(String.format("Expected json element to be a object type in the JSON string but got `%s`", jsonElement.toString()));
+ }
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ {{#items.isModel}}
+ {{{items.dataType}}}.validateJsonElement(entry.getValue());
+ {{/items.isModel}}
+ {{^items.isModel}}
+ {{#items.isUuid}}
+ UUID.fromString(entry.getValue().getAsString());
+ {{/items.isUuid}}
+ {{^items.isUuid}}
+ if (!entry.getValue().isJsonPrimitive()) {
+ throw new IllegalArgumentException(String.format("Expected map values to be a primitive type in the JSON string but got `%s`", entry.getValue().toString()));
+ }
+ {{/items.isUuid}}
+ {{/items.isModel}}
+ }
+ {{/isMap}}
+ {{^isMap}}
+ {{#isUuid}}
+ UUID.fromString(jsonElement.getAsString());
+ {{/isUuid}}
+ {{^isUuid}}
+ {{{dataType}}}.validateJsonElement(jsonElement);
+ {{/isUuid}}
+ {{/isMap}}
+ {{/isArray}}
+ {{/isPrimitiveType}}
+ {{/isNumber}}
+ {{/isModel}}
+ {{classname}} ret = new {{classname}}();
+ ret.setActualInstance(adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.fromJsonTree(jsonElement));
+ {{#isAdditionalPropertiesTrue}}
+ collectUnconsumedProperties(ret, jsonElement, gson);
+ {{/isAdditionalPropertiesTrue}}
+ return ret;
+ } catch (Exception e) {
+ // deserialization failed, continue
+ errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.getMessage()));
+ log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e);
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/anyOf}}
+ {{/composedSchemas}}
+
+ throw new IOException(String.format("Failed deserialization for {{classname}}: no match found. %s", errorMessages));
+ }
+ };
+ }
+ }
+ {{/isGson}}
+
+ {{#isJackson}}
+ public static class {{classname}}Serializer extends StdSerializer<{{classname}}> {
+ public {{classname}}Serializer(Class<{{classname}}> t) {
+ super(t);
+ }
+
+ public {{classname}}Serializer() {
+ this(null);
+ }
+
+ @Override
+ public void serialize({{classname}} value, JsonGenerator jgen, {{^useJackson3}}SerializerProvider provider{{/useJackson3}}{{#useJackson3}}SerializationContext serializationContext{{/useJackson3}}) throws {{^useJackson3}}IOException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
+ {{#isAdditionalPropertiesTrue}}
+ {{! @JsonAnyGetter is ignored on a class with a custom serializer, so the unconsumed
+ properties have to be merged into the child's tree by hand. }}
+ if (value.getActualInstance() != null && value.getAdditionalProperties() != null
+ && !value.getAdditionalProperties().isEmpty()) {
+ JsonNode node = JSON.getMapper().valueToTree(value.getActualInstance());
+ if (node != null && node.isObject()) {
+ {{jacksonPackage}}.databind.node.ObjectNode objectNode = ({{jacksonPackage}}.databind.node.ObjectNode) node;
+ for (Map.Entry entry : value.getAdditionalProperties().entrySet()) {
+ if (objectNode.has(entry.getKey())) {
+ // the child schema owns this property
+ continue;
+ }
+ objectNode.set(entry.getKey(), JSON.getMapper().valueToTree(entry.getValue()));
+ }
+ {{^useJackson3}}
+ jgen.writeTree(node);
+ {{/useJackson3}}
+ {{#useJackson3}}
+ serializationContext.writeValue(jgen, node);
+ {{/useJackson3}}
+ return;
+ }
+ }
+ {{/isAdditionalPropertiesTrue}}
+ {{^useJackson3}}
+ jgen.writeObject(value.getActualInstance());
+ {{/useJackson3}}
+ {{#useJackson3}}
+ serializationContext.writeValue(jgen, value.getActualInstance());
+ {{/useJackson3}}
+ }
+ }
+
+ public static class {{classname}}Deserializer extends StdDeserializer<{{classname}}> {
+ public {{classname}}Deserializer() {
+ this({{classname}}.class);
+ }
+
+ public {{classname}}Deserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public {{classname}} deserialize(JsonParser jp, DeserializationContext ctxt) throws {{^useJackson3}}IOException{{/useJackson3}}{{#useJackson3}}JacksonException{{/useJackson3}} {
+ JsonNode tree = ctxt.readTree(jp);
+ Object deserialized = null;
+ {{#discriminator}}
+ Class> cls = JSON.getClassForElement(tree, new {{classname}}().getClass());
+ if (cls != null) {
+ {{^useJackson3}}
+ deserialized = tree.traverse(jp.getCodec()).readValueAs(cls);
+ {{/useJackson3}}
+ {{#useJackson3}}
+ deserialized = ctxt.readTreeAsValue(tree, ctxt.constructType(cls));
+ {{/useJackson3}}
+ {{classname}} ret = new {{classname}}();
+ ret.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ collectUnconsumedProperties(ret, tree);
+ {{/isAdditionalPropertiesTrue}}
+ return ret;
+ }
+ {{/discriminator}}
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ // deserialize {{{dataType}}}
+ try {
+ {{^useJackson3}}
+ deserialized = tree.traverse(jp.getCodec()).readValueAs(new TypeReference<{{{dataType}}}>() {});
+ {{/useJackson3}}
+ {{#useJackson3}}
+ deserialized = ctxt.readTreeAsValue(tree, ctxt.getTypeFactory().constructType(new TypeReference<{{{dataType}}}>() {}));
+ {{/useJackson3}}
+ {{classname}} ret = new {{classname}}();
+ ret.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ collectUnconsumedProperties(ret, tree);
+ {{/isAdditionalPropertiesTrue}}
+ return ret;
+ } catch (Exception e) {
+ // deserialization failed, continue
+ log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e);
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.anyOf}}
+ {{^useJackson3}}
+ throw new IOException(String.format(java.util.Locale.ROOT, "Failed deserialization for {{classname}}: no match found"));
+ {{/useJackson3}}
+ {{#useJackson3}}
+ throw DatabindException.from(jp, String.format(java.util.Locale.ROOT, "Failed deserialization for {{classname}}: no match found"));
+ {{/useJackson3}}
+ }
+
+ /**
+ * Handle deserialization of the 'null' value.
+ */
+ @Override
+ {{^useJackson3}}
+ public {{classname}} getNullValue(DeserializationContext ctxt) throws JsonMappingException {
+ {{/useJackson3}}
+ {{#useJackson3}}
+ public {{classname}} getNullValue(DeserializationContext ctxt) {
+ {{/useJackson3}}
+ {{#isNullable}}
+ return null;
+ {{/isNullable}}
+ {{^isNullable}}
+ {{^useJackson3}}
+ throw new JsonMappingException(ctxt.getParser(), "{{classname}} cannot be null");
+ {{/useJackson3}}
+ {{#useJackson3}}
+ throw DatabindException.from(ctxt.getParser(), "{{classname}} cannot be null");
+ {{/useJackson3}}
+ {{/isNullable}}
+ }
+ }
+ {{/isJackson}}
+
+ {{#isJsonb}}
+ public static class {{classname}}Serializer implements JsonbSerializer<{{classname}}> {
+ @Override
+ public void serialize({{classname}} obj, JsonGenerator generator, SerializationContext ctx) {
+ if (obj.getActualInstance() == null) {
+ generator.writeNull();
+ return;
+ }
+ {{#isAdditionalPropertiesTrue}}
+ if (obj.getAdditionalProperties() != null && !obj.getAdditionalProperties().isEmpty()) {
+ JsonObject childObject = toJsonObject(obj.getActualInstance());
+ if (childObject != null) {
+ jakarta.json.JsonObjectBuilder builder = jakarta.json.Json.createObjectBuilder(childObject);
+ for (Map.Entry entry : obj.getAdditionalProperties().entrySet()) {
+ if (childObject.containsKey(entry.getKey())) {
+ // the child schema owns this property
+ continue;
+ }
+ builder.add(entry.getKey(), toJsonValue(entry.getValue()));
+ }
+ ctx.serialize(builder.build(), generator);
+ return;
+ }
+ }
+ {{/isAdditionalPropertiesTrue}}
+ ctx.serialize(obj.getActualInstance(), generator);
+ }
+ }
+ {{#isAdditionalPropertiesTrue}}
+
+ /**
+ * Render a value through JSON-B and read it back as a JsonObject, or null if it is not an object.
+ */
+ private static JsonObject toJsonObject(Object value) {
+ try (jakarta.json.JsonReader reader = jakarta.json.Json.createReader(
+ new java.io.StringReader(JSON.getJsonb().toJson(value)))) {
+ jakarta.json.JsonStructure structure = reader.read();
+ return structure instanceof JsonObject ? (JsonObject) structure : null;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static jakarta.json.JsonValue toJsonValue(Object value) {
+ if (value == null) {
+ return jakarta.json.JsonValue.NULL;
+ }
+ try (jakarta.json.JsonReader reader = jakarta.json.Json.createReader(
+ new java.io.StringReader(JSON.getJsonb().toJson(value)))) {
+ return reader.readValue();
+ }
+ }
+
+ /**
+ * Record the properties that the matched anyOf schema did not consume.
+ *
+ * The wrapper only owns what the selected child schema left behind. When that child accepts
+ * additional properties itself it absorbs them all and this records nothing, so a property is
+ * never stored — and therefore never written — twice.
+ */
+ private static void collectUnconsumedProperties({{classname}} instance, JsonObject jsonObject) {
+ if (jsonObject == null || instance.getActualInstance() == null) {
+ return;
+ }
+ JsonObject consumed = toJsonObject(instance.getActualInstance());
+ if (consumed == null) {
+ return;
+ }
+ for (Map.Entry entry : jsonObject.entrySet()) {
+ if (consumed.containsKey(entry.getKey())) {
+ continue;
+ }
+ instance.putAdditionalProperty(entry.getKey(), fromJsonValue(entry.getValue()));
+ }
+ }
+
+ private static Object fromJsonValue(jakarta.json.JsonValue value) {
+ switch (value.getValueType()) {
+ case STRING:
+ return ((jakarta.json.JsonString) value).getString();
+ case NUMBER:
+ return ((jakarta.json.JsonNumber) value).numberValue();
+ case TRUE:
+ return Boolean.TRUE;
+ case FALSE:
+ return Boolean.FALSE;
+ case NULL:
+ return null;
+ default:
+ return JSON.getJsonb().fromJson(value.toString(),
+ value.getValueType() == jakarta.json.JsonValue.ValueType.ARRAY
+ ? List.class : HashMap.class);
+ }
+ }
+ {{/isAdditionalPropertiesTrue}}
+
+ {{! The DeserializationContext handed to a JsonbDeserializer belongs to the parse that is
+ already running: Yasson wraps whatever parser reaches ctx.deserialize in a YassonParser
+ whose scope bookkeeping is taken from the context, not from that parser. Handing it a
+ second, freshly created parser therefore produces a wrapper that believes it already
+ sits at the end of the outer value, so the branch's own deserializer fails in
+ getObject()/getValue() with NoSuchElementException or an NPE - and once one attempt has
+ left those counters askew a later branch binds whatever it is handed. Bind the branch
+ through the binding API instead, which runs a parse of its own. }}
+ private static Object deserializeBranch(Class> type, JsonValue value) {
+ if (!branchAcceptsValueType(type, value)) {
+ throw new IllegalArgumentException(String.format(java.util.Locale.ROOT,
+ "Expected the JSON value to bind as %s but got `%s`", type.getSimpleName(), value));
+ }
+ {{! getJsonb(), not getPlainJsonb(): a branch can itself be a discriminated-hierarchy
+ root, and the concrete-subtype dispatch for those lives in a deserializer that only
+ the main instance carries. Binding a branch never re-enters this wrapper, so there
+ is no recursion to break by dropping to the plain instance. }}
+ return JSON.getJsonb().fromJson(value.toString(), type);
+ }
+
+ {{! Yasson binds by coercion, so a branch whose Java type has nothing to do with the JSON
+ value at hand still "matches": String swallows a number or a boolean and a numeric type
+ swallows a numeric string, which leaves a composed schema of scalars permanently
+ ambiguous. Gate on the JSON value type first - the Gson branch of this template does
+ the same thing through its generated per-branch validateJsonElement checks. }}
+ private static boolean branchAcceptsValueType(Class> type, JsonValue value) {
+ if (type == Object.class) {
+ // an 'any type' branch takes whatever it is handed
+ return true;
+ }
+ if (AbstractOpenApiSchema.class.isAssignableFrom(type)) {
+ // a branch that is itself a composed schema decides for itself which JSON shapes
+ // it accepts - an anyOf of scalars nested inside another composed schema binds a
+ // scalar here
+ return true;
+ }
+ boolean sequence = java.util.Collection.class.isAssignableFrom(type)
+ || (type.isArray() && type != byte[].class);
+ switch (value.getValueType()) {
+ case ARRAY:
+ return sequence;
+ case OBJECT:
+ return !sequence && !bindsFromJsonScalar(type);
+ case TRUE:
+ case FALSE:
+ return type == Boolean.class || type == boolean.class;
+ case NUMBER:
+ return Number.class.isAssignableFrom(type)
+ || (type.isPrimitive() && type != boolean.class && type != char.class);
+ case STRING:
+ return !sequence
+ && !Number.class.isAssignableFrom(type)
+ && type != Boolean.class && type != boolean.class;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Whether a branch of this type binds from a JSON scalar rather than from a JSON object.
+ */
+ private static boolean bindsFromJsonScalar(Class> type) {
+ {{! byte[] is a scalar here on purpose: 'format: byte' arrives as a base64 string. }}
+ return type.isPrimitive()
+ || Number.class.isAssignableFrom(type)
+ || type == Boolean.class
+ || type == Character.class
+ || CharSequence.class.isAssignableFrom(type)
+ || type == java.util.UUID.class
+ || type == byte[].class
+ || type.isEnum();
+ }
+
+ public static class {{classname}}Deserializer implements JsonbDeserializer<{{classname}}> {
+ @Override
+ public {{classname}} deserialize(JsonParser parser, DeserializationContext ctx, Type rt) {
+ {{! A composed branch can be an array or a scalar, not only an object, and
+ parser.getObject() throws IllegalStateException on START_ARRAY. Read the
+ generic JsonValue and narrow only for the object-shaped paths below. }}
+ JsonValue jsonValue = parser.getValue();
+ {{! Yasson routes a JSON null through this deserializer instead of short-circuiting
+ it, and every branch then fails to bind. Mirror the Jackson branch's
+ getNullValue: a nullable composed schema yields null, a non-nullable one is an
+ error. }}
+ if (jsonValue == null || jsonValue.getValueType() == JsonValue.ValueType.NULL) {
+ {{#isNullable}}
+ return null;
+ {{/isNullable}}
+ {{^isNullable}}
+ throw new RuntimeException("{{classname}} cannot be null");
+ {{/isNullable}}
+ }
+ JsonObject jsonObject = jsonValue instanceof JsonObject ? (JsonObject) jsonValue : null;
+ Object deserialized = null;
+ {{#useOneOfDiscriminatorLookup}}
+ {{#discriminator}}
+ if (jsonObject != null && jsonObject.containsKey("{{{propertyBaseName}}}")) {
+ String discriminatorValue = jsonObject.getString("{{{propertyBaseName}}}");
+ {{#mappedModels}}
+ if ("{{{mappingName}}}".equals(discriminatorValue)) {
+ deserialized = deserializeBranch({{{modelName}}}.class, jsonValue);
+ {{classname}} new{{classname}} = new {{classname}}();
+ new{{classname}}.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ if (jsonObject != null) {
+ collectUnconsumedProperties(new{{classname}}, jsonObject);
+ }
+ {{/isAdditionalPropertiesTrue}}
+ return new{{classname}};
+ }
+ {{/mappedModels}}
+ }
+ {{/discriminator}}
+ {{/useOneOfDiscriminatorLookup}}
+ int match = 0;
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ // deserialize {{{dataType}}}
+ try {
+ deserialized = deserializeBranch({{{baseType}}}.class, jsonValue);
+ match++;
+ {{classname}} ret = new {{classname}}();
+ ret.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ if (jsonObject != null) {
+ collectUnconsumedProperties(ret, jsonObject);
+ }
+ {{/isAdditionalPropertiesTrue}}
+ return ret;
+ } catch (Exception e) {
+ // deserialization failed, continue
+ }
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.anyOf}}
+ if (match > 0) {
+ {{classname}} ret = new {{classname}}();
+ ret.setActualInstance(deserialized);
+ {{#isAdditionalPropertiesTrue}}
+ if (jsonObject != null) {
+ collectUnconsumedProperties(ret, jsonObject);
+ }
+ {{/isAdditionalPropertiesTrue}}
+ return ret;
+ }
+ throw new RuntimeException(String.format(java.util.Locale.ROOT, "Failed deserialization for {{classname}}: no match found"));
+ }
+ }
+ {{/isJsonb}}
+
+ // store a list of schema names defined in anyOf
+ public static final Map> schemas = new HashMap<>();
+
+ public {{classname}}() {
+ super("anyOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}});
+ }
+
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ public {{classname}}({{{dataType}}} o) {
+ super("anyOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}});
+ setActualInstance(o);
+ }
+ {{/vendorExtensions.x-duplicated-data-type}}
+
+ {{/composedSchemas.anyOf}}
+ static {
+ {{#composedSchemas.anyOf}}
+ schemas.put("{{{dataType}}}", {{{baseType}}}.class);
+ {{/composedSchemas.anyOf}}
+ {{#isJackson}}
+ JSON.registerDescendants({{classname}}.class, Collections.unmodifiableMap(schemas));
+ {{#discriminator}}
+ // Initialize and register the discriminator mappings.
+ Map> mappings = new HashMap>();
+ {{#mappedModels}}
+ mappings.put("{{mappingName}}", {{modelName}}.class);
+ {{/mappedModels}}
+ mappings.put("{{name}}", {{classname}}.class);
+ JSON.registerDiscriminator({{classname}}.class, "{{propertyBaseName}}", mappings);
+ {{/discriminator}}
+ {{/isJackson}}
+ }
+
+ @Override
+ public Map> getSchemas() {
+ return {{classname}}.schemas;
+ }
+
+ /**
+ * Set the instance that matches the anyOf child schema, check
+ * the instance parameter is valid against the anyOf child schemas:
+ * {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}
+ *
+ * It could be an instance of the 'anyOf' schemas.
+ * The anyOf child schemas may themselves be a composed schema (allOf, anyOf, anyOf).
+ */
+ @Override
+ public void setActualInstance(Object instance) {
+ {{#isNullable}}
+ if (instance == null) {
+ super.setActualInstance(instance);
+ return;
+ }
+
+ {{/isNullable}}
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ if (JSON.isInstanceOf({{{baseType}}}.class, instance, new HashSet>())) {
+ super.setActualInstance(instance);
+ return;
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.anyOf}}
+ throw new RuntimeException("Invalid instance type. Must be {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
+ }
+
+ /**
+ * Get the actual instance, which can be the following:
+ * {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}
+ *
+ * @return The actual instance ({{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}})
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object getActualInstance() {
+ return super.getActualInstance();
+ }
+
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type-ignoring-erasure}}
+ /**
+ * Get the actual instance of `{{{dataType}}}`. If the actual instance is not `{{{dataType}}}`,
+ * the ClassCastException will be thrown.
+ *
+ * @return The actual instance of `{{{dataType}}}`
+ * @throws ClassCastException if the instance is not `{{{dataType}}}`
+ */
+ @SuppressWarnings("unchecked")
+ public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
+ return ({{{dataType}}})super.getActualInstance();
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type-ignoring-erasure}}
+ {{/composedSchemas.anyOf}}
+
+{{#supportUrlQuery}}
+
+ /**
+ * Convert the instance into URL query string.
+ *
+ * @return URL query string
+ */
+ public String toUrlQueryString() {
+ return toUrlQueryString(null);
+ }
+
+ /**
+ * Convert the instance into URL query string.
+ *
+ * @param prefix prefix of the query string
+ * @return URL query string
+ */
+ public String toUrlQueryString(String prefix) {
+ String suffix = "";
+ String containerSuffix = "";
+ String containerPrefix = "";
+ if (prefix == null) {
+ // style=form, explode=true, e.g. /pet?name=cat&type=manx
+ prefix = "";
+ } else {
+ // deepObject style e.g. /pet?id[name]=cat&id[type]=manx
+ prefix = prefix + "[";
+ suffix = "]";
+ containerSuffix = "]";
+ containerPrefix = "[";
+ }
+
+ StringJoiner joiner = new StringJoiner("&");
+
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ if (getActualInstance() instanceof {{{dataType}}}) {
+ {{#isArray}}
+ {{#items.isPrimitiveType}}
+ {{#uniqueItems}}
+ if (getActualInstance() != null) {
+ int i = 0;
+ for ({{{items.dataType}}} _item : ({{{dataType}}})getActualInstance()) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s=%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix),
+ ApiClient.urlEncode(String.valueOf(_item))));
+ }
+ i++;
+ }
+ {{/uniqueItems}}
+ {{^uniqueItems}}
+ if (getActualInstance() != null) {
+ for (int i = 0; i < (({{{dataType}}})getActualInstance()).size(); i++) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s=%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix),
+ ApiClient.urlEncode(String.valueOf((({{{dataType}}})getActualInstance()).get(i)))));
+ }
+ }
+ {{/uniqueItems}}
+ {{/items.isPrimitiveType}}
+ {{^items.isPrimitiveType}}
+ {{#items.isModel}}
+ {{#uniqueItems}}
+ if (getActualInstance() != null) {
+ int i = 0;
+ for ({{{items.dataType}}} _item : ({{{dataType}}})getActualInstance()) {
+ if (_item != null) {
+ joiner.add(_item.toUrlQueryString(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix))));
+ }
+ }
+ i++;
+ }
+ {{/uniqueItems}}
+ {{^uniqueItems}}
+ if (getActualInstance() != null) {
+ for (int i = 0; i < (({{{dataType}}})getActualInstance()).size(); i++) {
+ if ((({{{dataType}}})getActualInstance()).get(i) != null) {
+ joiner.add((({{{dataType}}})getActualInstance()).get(i).toUrlQueryString(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix))));
+ }
+ }
+ }
+ {{/uniqueItems}}
+ {{/items.isModel}}
+ {{^items.isModel}}
+ {{#uniqueItems}}
+ if (getActualInstance() != null) {
+ int i = 0;
+ for ({{{items.dataType}}} _item : ({{{dataType}}})getActualInstance()) {
+ if (_item != null) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s=%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix),
+ ApiClient.urlEncode(String.valueOf(_item))));
+ }
+ i++;
+ }
+ }
+ {{/uniqueItems}}
+ {{^uniqueItems}}
+ if (getActualInstance() != null) {
+ for (int i = 0; i < (({{{dataType}}})getActualInstance()).size(); i++) {
+ if (getActualInstance().get(i) != null) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s=%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, i, containerSuffix),
+ ApiClient.urlEncode(String.valueOf((({{{dataType}}})getActualInstance()).get(i)))));
+ }
+ }
+ }
+ {{/uniqueItems}}
+ {{/items.isModel}}
+ {{/items.isPrimitiveType}}
+ {{/isArray}}
+ {{^isArray}}
+ {{#isMap}}
+ {{#items.isPrimitiveType}}
+ if (getActualInstance() != null) {
+ for (String _key : (({{{dataType}}})getActualInstance()).keySet()) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s=%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, _key, containerSuffix),
+ getActualInstance().get(_key), ApiClient.urlEncode(String.valueOf((({{{dataType}}})getActualInstance()).get(_key)))));
+ }
+ }
+ {{/items.isPrimitiveType}}
+ {{^items.isPrimitiveType}}
+ if (getActualInstance() != null) {
+ for (String _key : (({{{dataType}}})getActualInstance()).keySet()) {
+ if ((({{{dataType}}})getActualInstance()).get(_key) != null) {
+ joiner.add((({{{items.dataType}}})getActualInstance()).get(_key).toUrlQueryString(String.format(java.util.Locale.ROOT, "%s{{baseName}}%s%s", prefix, suffix,
+ "".equals(suffix) ? "" : String.format(java.util.Locale.ROOT, "%s%d%s", containerPrefix, _key, containerSuffix))));
+ }
+ }
+ }
+ {{/items.isPrimitiveType}}
+ {{/isMap}}
+ {{^isMap}}
+ {{#isPrimitiveType}}
+ if (getActualInstance() != null) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{{baseName}}}%s=%s", prefix, suffix, ApiClient.urlEncode(String.valueOf(getActualInstance()))));
+ }
+ {{/isPrimitiveType}}
+ {{^isPrimitiveType}}
+ {{#isModel}}
+ if (getActualInstance() != null) {
+ joiner.add((({{{dataType}}})getActualInstance()).toUrlQueryString(prefix + "{{{baseName}}}" + suffix));
+ }
+ {{/isModel}}
+ {{^isModel}}
+ if (getActualInstance() != null) {
+ joiner.add(String.format(java.util.Locale.ROOT, "%s{{{baseName}}}%s=%s", prefix, suffix, ApiClient.urlEncode(String.valueOf(getActualInstance()))));
+ }
+ {{/isModel}}
+ {{/isPrimitiveType}}
+ {{/isMap}}
+ {{/isArray}}
+ return joiner.toString();
+ }
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.anyOf}}
+ return null;
+ }
+{{/supportUrlQuery}}
+
+{{#isGson}}
+ /**
+ * Create an instance of {{classname}} given an JSON string
+ *
+ * @param jsonString JSON string
+ * @return An instance of {{classname}}
+ * @throws IOException if the JSON string is invalid with respect to {{classname}}
+ */
+ public static {{{classname}}} fromJson(String jsonString) throws IOException {
+ return JSON.getGson().fromJson(jsonString, {{{classname}}}.class);
+ }
+
+ /**
+ * Convert an instance of {{classname}} to an JSON string
+ *
+ * @return JSON string
+ */
+ public String toJson() {
+ return JSON.getGson().toJson(this);
+ }
+{{/isGson}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api.mustache
new file mode 100644
index 000000000000..b3ab0729371c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api.mustache
@@ -0,0 +1,634 @@
+{{>licenseInfo}}
+
+package {{package}};
+
+import {{invokerPackage}}.ApiCallback;
+import {{invokerPackage}}.ApiClient;
+import {{invokerPackage}}.ApiException;
+{{#dynamicOperations}}
+import {{invokerPackage}}.ApiOperation;
+{{/dynamicOperations}}
+import {{invokerPackage}}.ApiResponse;
+import {{invokerPackage}}.JSON;
+import {{invokerPackage}}.Configuration;
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ProgressRequestBody;
+import {{invokerPackage}}.ProgressResponseBody;
+{{#performBeanValidation}}
+import {{invokerPackage}}.BeanValidationException;
+{{/performBeanValidation}}
+
+{{#isGson}}
+import com.google.gson.reflect.TypeToken;
+{{/isGson}}
+{{#isJackson}}
+import {{jacksonPackage}}.core.type.TypeReference;
+{{/isJackson}}
+{{#dynamicOperations}}
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.parameters.Parameter;
+{{/dynamicOperations}}
+
+import java.io.IOException;
+
+{{#useBeanValidation}}
+import {{javaxPackage}}.validation.constraints.*;
+import {{javaxPackage}}.validation.Valid;
+{{/useBeanValidation}}
+{{#performBeanValidation}}
+import {{javaxPackage}}.validation.ConstraintViolation;
+import {{javaxPackage}}.validation.Validation;
+import {{javaxPackage}}.validation.ValidatorFactory;
+import {{javaxPackage}}.validation.executable.ExecutableValidator;
+import java.util.Set;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+{{/performBeanValidation}}
+
+{{#imports}}import {{import}};
+{{/imports}}
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+{{#supportStreaming}}
+import java.io.InputStream;
+{{/supportStreaming}}
+
+{{#operations}}
+public class {{classname}} {
+ private ApiClient localVarApiClient;
+ private int localHostIndex;
+ private String localCustomBaseUrl;
+
+ public {{classname}}() {
+ this(Configuration.getDefaultApiClient());
+ }
+
+ public {{classname}}(ApiClient apiClient) {
+ this.localVarApiClient = apiClient;
+ }
+
+ public ApiClient getApiClient() {
+ return localVarApiClient;
+ }
+
+ public void setApiClient(ApiClient apiClient) {
+ this.localVarApiClient = apiClient;
+ }
+
+ public int getHostIndex() {
+ return localHostIndex;
+ }
+
+ public void setHostIndex(int hostIndex) {
+ this.localHostIndex = hostIndex;
+ }
+
+ public String getCustomBaseUrl() {
+ return localCustomBaseUrl;
+ }
+
+ public void setCustomBaseUrl(String customBaseUrl) {
+ this.localCustomBaseUrl = customBaseUrl;
+ }
+
+ {{#operation}}
+ {{^vendorExtensions.x-group-parameters}}/**
+ * Build call for {{operationId}}{{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}{{/allParams}}
+ * @param _callback Callback for upload/download progress
+ * @return Call to execute
+ * @throws ApiException If fail to serialize the request body object
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}private{{/vendorExtensions.x-group-parameters}} okhttp3.Call {{operationId}}Call({{#allParams}}{{>nullableArgument}} {{paramName}}, {{/allParams}}final ApiCallback _callback) throws ApiException {
+ String basePath = null;
+ // Operation Servers
+ String[] localBasePaths = new String[] { {{#servers}}"{{{url}}}"{{^-last}}, {{/-last}}{{/servers}} };
+
+ // Determine Base Path to Use
+ if (localCustomBaseUrl != null){
+ basePath = localCustomBaseUrl;
+ } else if ( localBasePaths.length > 0 ) {
+ basePath = localBasePaths[localHostIndex];
+ } else {
+ basePath = null;
+ }
+
+ Object localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
+
+ // create path and map variables
+ {{^dynamicOperations}}
+ String localVarPath = "{{{path}}}"{{#pathParams}}
+ .replace("{" + "{{baseName}}" + "}", localVarApiClient.escapeString({{#collectionFormat}}localVarApiClient.collectionPathParameterToString("{{{collectionFormat}}}", {{{paramName}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}.toString(){{/collectionFormat}})){{/pathParams}};
+ {{/dynamicOperations}}
+ {{#dynamicOperations}}
+ ApiOperation apiOperation = localVarApiClient.getOperationLookupMap().get("{{{operationId}}}");
+ if (apiOperation == null) {
+ throw new ApiException("Operation not found in OAS");
+ }
+ Operation operation = apiOperation.getOperation();
+ String localVarPath = apiOperation.getPath();
+ Map paramMap = new HashMap<>();
+ {{#allParams}}
+ {{^isFormParam}}
+ {{^isBodyParam}}
+ paramMap.put("{{baseName}}", {{paramName}});
+ {{/isBodyParam}}
+ {{/isFormParam}}
+ {{/allParams}}
+ {{/dynamicOperations}}
+
+ List localVarQueryParams = new ArrayList();
+ List localVarCollectionQueryParams = new ArrayList();
+ Map localVarHeaderParams = new HashMap();
+ Map localVarCookieParams = new HashMap();
+ Map localVarFormParams = new HashMap();
+
+ {{#formParams}}
+ if ({{paramName}} != null) {
+ localVarFormParams.put("{{baseName}}", {{paramName}});
+ }
+
+ {{/formParams}}
+ {{^dynamicOperations}}
+ {{#queryParams}}
+ if ({{paramName}} != null) {
+ {{#isFreeFormObject}}localVarQueryParams.addAll(localVarApiClient.freeFormParameterToPairs({{paramName}}));{{/isFreeFormObject}}{{^isFreeFormObject}}{{#collectionFormat}}localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("{{{.}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll(localVarApiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}}));{{/isFreeFormObject}}
+ }
+
+ {{/queryParams}}
+ {{#constantParams}}
+ {{#isQueryParam}}
+ // Set client side default value of Query Param "{{baseName}}".
+ localVarCollectionQueryParams.add(new Pair("{{baseName}}", {{#_enum}}"{{{.}}}"{{/_enum}}));
+
+ {{/isQueryParam}}
+ {{/constantParams}}
+ {{#constantParams}}
+ {{#isHeaderParam}}
+ // Set client side default value of Header Param "{{baseName}}".
+ localVarHeaderParams.put("{{baseName}}", {{#_enum}}"{{{.}}}"{{/_enum}});
+
+ {{/isHeaderParam}}
+ {{/constantParams}}
+ {{#cookieParams}}
+ if ({{paramName}} != null) {
+ localVarCookieParams.put("{{baseName}}", localVarApiClient.parameterToString({{paramName}}));
+ }
+
+ {{/cookieParams}}
+ {{#constantParams}}
+ {{#isCookieParam}}
+ // Set client side default value of Cookie Param "{{baseName}}".
+ localVarCookieParams.put("{{baseName}}", {{#_enum}}"{{{.}}}"{{/_enum}});
+
+ {{/isCookieParam}}
+ {{/constantParams}}
+ {{/dynamicOperations}}
+ {{#dynamicOperations}}
+ localVarPath = localVarApiClient.fillParametersFromOperation(operation, paramMap, localVarPath, localVarQueryParams, localVarCollectionQueryParams, localVarHeaderParams, localVarCookieParams);
+
+ {{/dynamicOperations}}
+ final String[] localVarAccepts = {
+ {{#produces}}
+ "{{{mediaType}}}"{{^-last}},{{/-last}}
+ {{/produces}}
+ };
+ final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts);
+ if (localVarAccept != null) {
+ localVarHeaderParams.put("Accept", localVarAccept);
+ }
+
+ final String[] localVarContentTypes = {
+ {{#consumes}}
+ "{{{mediaType}}}"{{^-last}},{{/-last}}
+ {{/consumes}}
+ };
+ final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes);
+ if (localVarContentType != null) {
+ localVarHeaderParams.put("Content-Type", localVarContentType);
+ }
+ {{^dynamicOperations}}
+ {{#headerParams}}
+
+ if ({{paramName}} != null) {
+ localVarHeaderParams.put("{{baseName}}", localVarApiClient.parameterToString({{paramName}}));
+ }
+
+ {{/headerParams}}
+ {{/dynamicOperations}}
+
+ String[] localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{#-last}}{{#withAWSV4Signature}}, {{/withAWSV4Signature}}{{/-last}}{{/authMethods}}{{#withAWSV4Signature}}"AWS4Auth"{{/withAWSV4Signature}} };
+ return localVarApiClient.buildCall(basePath, localVarPath, {{^dynamicOperations}}"{{httpMethod}}"{{/dynamicOperations}}{{#dynamicOperations}}apiOperation.getMethod(){{/dynamicOperations}}, localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback);
+ }
+
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ @SuppressWarnings("rawtypes")
+ private okhttp3.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{>nullableArgument}} {{paramName}}, {{/allParams}}final ApiCallback _callback) throws ApiException {
+ {{! The required-parameter checks are deliberately NOT guarded by performBeanValidation.
+ They used to live inside the inverted performBeanValidation section, so enabling
+ bean validation REMOVED them and substituted a validator that never fires for a
+ plain null argument - turning a clear ApiException into a NullPointerException
+ further down, or letting the null reach the server. Bean validation is additive
+ on top of these checks, never a replacement. }}
+ {{#allParams}}
+ {{#required}}
+ // verify the required parameter '{{paramName}}' is set
+ if ({{paramName}} == null) {
+ throw new ApiException("Missing the required parameter '{{paramName}}' when calling {{operationId}}(Async)");
+ }
+
+ {{/required}}
+ {{/allParams}}
+ {{^performBeanValidation}}
+ return {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}_callback);
+
+ {{/performBeanValidation}}
+ {{#performBeanValidation}}
+ try {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ ExecutableValidator executableValidator = factory.getValidator().forExecutables();
+
+ Object[] parameterValues = { {{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}} };
+ Method method = this.getClass().getMethod("{{operationId}}WithHttpInfo"{{#allParams}}, {{#isArray}}java.util.List{{/isArray}}{{#isMap}}java.util.Map{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}}.class{{/allParams}});
+ Set> violations = executableValidator.validateParameters(this, method,
+ parameterValues);
+
+ if (violations.size() == 0) {
+ return {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}_callback);
+ } else {
+ throw new BeanValidationException((Set) violations);
+ }
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ throw new ApiException(e.getMessage());
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ throw new ApiException(e.getMessage());
+ }
+ {{/performBeanValidation}}
+ }
+
+ {{^vendorExtensions.x-group-parameters}}
+ /**
+ * {{summary}}
+ * {{notes}}{{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}{{/allParams}}{{#returnType}}
+ * @return {{.}}{{/returnType}}
+ * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ {{#vendorExtensions.x-streaming}}
+ public {{#returnType}}InputStream {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{>nullableArgument}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException {
+ {{#returnType}}InputStream localVarResp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}}
+ return localVarResp;{{/returnType}}
+ }
+ {{/vendorExtensions.x-streaming}}
+ {{^vendorExtensions.x-streaming}}
+ public {{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{>nullableArgument}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException {
+ {{#returnType}}ApiResponse<{{{.}}}> localVarResp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}}
+ return localVarResp.getData();{{/returnType}}
+ }
+ {{/vendorExtensions.x-streaming}}
+ {{/vendorExtensions.x-group-parameters}}
+
+ {{^vendorExtensions.x-group-parameters}}/**
+ * {{summary}}
+ * {{notes}}{{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}{{/allParams}}
+ * @return ApiResponse<{{returnType}}{{^returnType}}Void{{/returnType}}>
+ * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}private{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-streaming}} InputStream {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>nullable_var_annotations}} {{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException {
+ okhttp3.Call localVarCall = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null);
+ {{#returnType}}
+ {{#errorObjectType}}
+ try {
+ {{#isGson}}Type localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();{{/isGson}}
+ {{#isJackson}}Type localVarReturnType = new TypeReference<{{{returnType}}}>(){}.getType();{{/isJackson}}
+ {{#isJsonb}}Type localVarReturnType = new JSON.GenericType<{{{returnType}}}>(){}.getType();{{/isJsonb}}
+ return localVarApiClient.executeStream(localVarCall, localVarReturnType);
+ } catch (ApiException e) {
+ {{#isGson}}e.setErrorObject(localVarApiClient.getJSON().getGson().fromJson(e.getResponseBody(), new TypeToken<{{{errorObjectType}}}>(){}.getType()));{{/isGson}}
+ {{#isJackson}}e.setErrorObject(localVarApiClient.getJSON().deserialize(e.getResponseBody(), new TypeReference<{{{errorObjectType}}}>(){}.getType()));{{/isJackson}}
+ {{#isJsonb}}e.setErrorObject(localVarApiClient.getJSON().deserialize(e.getResponseBody(), new JSON.GenericType<{{{errorObjectType}}}>(){}.getType()));{{/isJsonb}}
+ throw e;
+ }
+ {{/errorObjectType}}
+ {{^errorObjectType}}
+ {{#isGson}}Type localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();{{/isGson}}
+ {{#isJackson}}Type localVarReturnType = new TypeReference<{{{returnType}}}>(){}.getType();{{/isJackson}}
+ {{#isJsonb}}Type localVarReturnType = new JSON.GenericType<{{{returnType}}}>(){}.getType();{{/isJsonb}}
+ return localVarApiClient.executeStream(localVarCall, localVarReturnType);
+ {{/errorObjectType}}
+ {{/returnType}}
+ }
+ {{/vendorExtensions.x-streaming}}{{^vendorExtensions.x-streaming}} ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>nullableArgument}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException {
+ okhttp3.Call localVarCall = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null);
+ {{^returnType}}
+ return localVarApiClient.execute(localVarCall);
+ {{/returnType}}
+ {{#returnType}}
+ {{#errorObjectType}}
+ try {
+ {{#isGson}}Type localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();{{/isGson}}
+ {{#isJackson}}Type localVarReturnType = new TypeReference<{{{returnType}}}>(){}.getType();{{/isJackson}}
+ {{#isJsonb}}Type localVarReturnType = new JSON.GenericType<{{{returnType}}}>(){}.getType();{{/isJsonb}}
+ return localVarApiClient.execute(localVarCall, localVarReturnType);
+ } catch (ApiException e) {
+ {{#isGson}}e.setErrorObject(localVarApiClient.getJSON().getGson().fromJson(e.getResponseBody(), new TypeToken<{{{errorObjectType}}}>(){}.getType()));{{/isGson}}
+ {{#isJackson}}e.setErrorObject(localVarApiClient.getJSON().deserialize(e.getResponseBody(), new TypeReference<{{{errorObjectType}}}>(){}.getType()));{{/isJackson}}
+ {{#isJsonb}}e.setErrorObject(localVarApiClient.getJSON().deserialize(e.getResponseBody(), new JSON.GenericType<{{{errorObjectType}}}>(){}.getType()));{{/isJsonb}}
+ throw e;
+ }
+ {{/errorObjectType}}
+ {{^errorObjectType}}
+ {{#isGson}}Type localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();{{/isGson}}
+ {{#isJackson}}Type localVarReturnType = new TypeReference<{{{returnType}}}>(){}.getType();{{/isJackson}}
+ {{#isJsonb}}Type localVarReturnType = new JSON.GenericType<{{{returnType}}}>(){}.getType();{{/isJsonb}}
+ return localVarApiClient.execute(localVarCall, localVarReturnType);
+ {{/errorObjectType}}
+ {{/returnType}}
+ }
+ {{/vendorExtensions.x-streaming}}
+
+ {{^vendorExtensions.x-group-parameters}}/**
+ * {{summary}} (asynchronously)
+ * {{notes}}{{#allParams}}
+ * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}{{/allParams}}
+ * @param _callback The callback to be executed when the API call finishes
+ * @return The request call
+ * @throws ApiException If fail to process the API call, e.g. serializing the request body object
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}private{{/vendorExtensions.x-group-parameters}} okhttp3.Call {{operationId}}Async({{#allParams}}{{>nullableArgument}} {{paramName}}, {{/allParams}}final ApiCallback<{{{returnType}}}{{^returnType}}Void{{/returnType}}> _callback) throws ApiException {
+
+ okhttp3.Call localVarCall = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}_callback);
+ {{#returnType}}{{#isGson}}Type localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType();{{/isGson}}{{#isJackson}}Type localVarReturnType = new TypeReference<{{{returnType}}}>(){}.getType();{{/isJackson}}{{#isJsonb}}Type localVarReturnType = new JSON.GenericType<{{{returnType}}}>(){}.getType();{{/isJsonb}}
+ localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback);{{/returnType}}{{^returnType}}localVarApiClient.executeAsync(localVarCall, _callback);{{/returnType}}
+ return localVarCall;
+ }
+ {{#vendorExtensions.x-group-parameters}}
+
+ public class API{{operationId}}Request {
+ {{#requiredParams}}
+ {{>nullable_var_annotations}}{{! prevent indent}}
+ private final {{#lambda.jSpecifyDatatype}}{{{dataType}}}{{/lambda.jSpecifyDatatype}} {{paramName}};
+ {{/requiredParams}}
+ {{#optionalParams}}
+ {{>nullable_var_annotations}}{{! prevent indent}}
+ private {{#lambda.jSpecifyDatatype}}{{{dataType}}}{{/lambda.jSpecifyDatatype}} {{paramName}};
+ {{/optionalParams}}
+
+ private API{{operationId}}Request({{#requiredParams}}{{>nullableArgument}} {{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}) {
+ {{#requiredParams}}
+ this.{{paramName}} = {{paramName}};
+ {{/requiredParams}}
+ }
+
+ {{#optionalParams}}
+ /**
+ * Set {{paramName}}
+ * @param {{paramName}} {{description}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}})
+ * @return API{{operationId}}Request
+ */
+ public API{{operationId}}Request {{paramName}}({{>nullableArgument}} {{paramName}}) {
+ this.{{paramName}} = {{paramName}};
+ return this;
+ }
+
+ {{/optionalParams}}
+ /**
+ * Build call for {{operationId}}
+ * @param _callback ApiCallback API callback
+ * @return Call to execute
+ * @throws ApiException If fail to serialize the request body object
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException {
+ return {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}_callback);
+ }
+
+ /**
+ * Execute {{operationId}} request{{#returnType}}
+ * @return {{.}}{{/returnType}}
+ * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ {{^vendorExtensions.x-streaming}}
+ public {{{returnType}}}{{^returnType}}void{{/returnType}} execute() throws ApiException {
+ {{#returnType}}ApiResponse<{{{.}}}> localVarResp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}}
+ return localVarResp.getData();{{/returnType}}
+ }
+ {{/vendorExtensions.x-streaming}}
+ {{#vendorExtensions.x-streaming}}
+ public InputStream execute() throws ApiException {
+ return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+ }
+ {{/vendorExtensions.x-streaming}}
+
+ /**
+ * Execute {{operationId}} request with HTTP info returned
+ * @return ApiResponse<{{returnType}}{{^returnType}}Void{{/returnType}}>
+ * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ {{^vendorExtensions.x-streaming}}
+ public ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}> executeWithHttpInfo() throws ApiException {
+ return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+ }
+ {{/vendorExtensions.x-streaming}}
+ {{#vendorExtensions.x-streaming}}
+ public InputStream executeWithHttpInfo() throws ApiException {
+ return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+ }
+ {{/vendorExtensions.x-streaming}}
+
+ /**
+ * Execute {{operationId}} request (asynchronously)
+ * @param _callback The callback to be executed when the API call finishes
+ * @return The request call
+ * @throws ApiException If fail to process the API call, e.g. serializing the request body object
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public okhttp3.Call executeAsync(final ApiCallback<{{{returnType}}}{{^returnType}}Void{{/returnType}}> _callback) throws ApiException {
+ return {{operationId}}Async({{#allParams}}{{paramName}}, {{/allParams}}_callback);
+ }
+ }
+
+ /**
+ * {{summary}}
+ * {{notes}}{{#requiredParams}}
+ * @param {{paramName}} {{description}} (required){{/requiredParams}}
+ * @return API{{operationId}}Request
+ {{#responses.0}}
+ * @http.response.details
+
+ Response Details
+ | Status Code | Description | Response Headers |
+ {{#responses}}
+ | {{code}} | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+ {{/responses}}
+
+ {{/responses.0}}
+ {{#isDeprecated}}
+ * @deprecated
+ {{/isDeprecated}}
+ {{#externalDocs}}
+ * {{description}}
+ * @see {{summary}} Documentation
+ {{/externalDocs}}
+ */
+ {{#isDeprecated}}
+ @Deprecated
+ {{/isDeprecated}}
+ public API{{operationId}}Request {{operationId}}({{#requiredParams}}{{>nullable_var_annotations}} {{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}) {
+ return new API{{operationId}}Request({{#requiredParams}}{{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}});
+ }
+ {{/vendorExtensions.x-group-parameters}}
+ {{/operation}}
+}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/apiException.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/apiException.mustache
new file mode 100644
index 000000000000..96283bc054d3
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/apiException.mustache
@@ -0,0 +1,196 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}};
+
+import java.util.Map;
+import java.util.List;
+{{#caseInsensitiveResponseHeaders}}
+import java.util.Map.Entry;
+import java.util.TreeMap;
+{{/caseInsensitiveResponseHeaders}}
+
+
+/**
+ * ApiException class.
+ */
+@SuppressWarnings("serial")
+{{>generatedAnnotation}}
+
+public class ApiException extends{{#useRuntimeException}} RuntimeException {{/useRuntimeException}}{{^useRuntimeException}} Exception {{/useRuntimeException}}{
+ private static final long serialVersionUID = 1L;
+
+ private int code = 0;
+ private Map> responseHeaders = null;
+ private String responseBody = null;
+ {{#errorObjectType}}
+ private {{{errorObjectType}}} errorObject = null;
+ {{/errorObjectType}}
+
+ /**
+ * Constructor for ApiException.
+ */
+ public ApiException() {}
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param throwable a {@link java.lang.Throwable} object
+ */
+ public ApiException(Throwable throwable) {
+ super(throwable);
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param message the error message
+ */
+ public ApiException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param message the error message
+ * @param throwable a {@link java.lang.Throwable} object
+ * @param code HTTP status code
+ * @param responseHeaders a {@link java.util.Map} of HTTP response headers
+ * @param responseBody the response body
+ */
+ public ApiException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) {
+ super(message, throwable);
+ this.code = code;
+ {{#caseInsensitiveResponseHeaders}}
+ Map> headers = new TreeMap>(String.CASE_INSENSITIVE_ORDER);
+ for(Entry> entry : responseHeaders.entrySet()){
+ headers.put(entry.getKey().toLowerCase(), entry.getValue());
+ }
+ {{/caseInsensitiveResponseHeaders}}
+ this.responseHeaders = {{#caseInsensitiveResponseHeaders}}headers{{/caseInsensitiveResponseHeaders}}{{^caseInsensitiveResponseHeaders}}responseHeaders{{/caseInsensitiveResponseHeaders}};
+ this.responseBody = responseBody;
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param message the error message
+ * @param code HTTP status code
+ * @param responseHeaders a {@link java.util.Map} of HTTP response headers
+ * @param responseBody the response body
+ */
+ public ApiException(String message, int code, Map> responseHeaders, String responseBody) {
+ this(message, (Throwable) null, code, responseHeaders, responseBody);
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param message the error message
+ * @param throwable a {@link java.lang.Throwable} object
+ * @param code HTTP status code
+ * @param responseHeaders a {@link java.util.Map} of HTTP response headers
+ */
+ public ApiException(String message, Throwable throwable, int code, Map> responseHeaders) {
+ this(message, throwable, code, responseHeaders, null);
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param code HTTP status code
+ * @param responseHeaders a {@link java.util.Map} of HTTP response headers
+ * @param responseBody the response body
+ */
+ public ApiException(int code, Map> responseHeaders, String responseBody) {
+ this("Response Code: " + code + " Response Body: " + responseBody, (Throwable) null, code, responseHeaders, responseBody);
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param code HTTP status code
+ * @param message a {@link java.lang.String} object
+ */
+ public ApiException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ /**
+ * Constructor for ApiException.
+ *
+ * @param code HTTP status code
+ * @param message the error message
+ * @param responseHeaders a {@link java.util.Map} of HTTP response headers
+ * @param responseBody the response body
+ */
+ public ApiException(int code, String message, Map> responseHeaders, String responseBody) {
+ this(code, message);
+ {{#caseInsensitiveResponseHeaders}}
+ Map> headers = new TreeMap>(String.CASE_INSENSITIVE_ORDER);
+ for(Entry> entry : responseHeaders.entrySet()){
+ headers.put(entry.getKey().toLowerCase(), entry.getValue());
+ }
+ {{/caseInsensitiveResponseHeaders}}
+ this.responseHeaders = {{#caseInsensitiveResponseHeaders}}headers{{/caseInsensitiveResponseHeaders}}{{^caseInsensitiveResponseHeaders}}responseHeaders{{/caseInsensitiveResponseHeaders}};
+ this.responseBody = responseBody;
+ }
+
+ /**
+ * Get the HTTP status code.
+ *
+ * @return HTTP status code
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Get the HTTP response headers.
+ *
+ * @return A map of list of string
+ */
+ public Map> getResponseHeaders() {
+ return responseHeaders;
+ }
+
+ /**
+ * Get the HTTP response body.
+ *
+ * @return Response body in the form of string
+ */
+ public String getResponseBody() {
+ return responseBody;
+ }
+
+ /**
+ * Get the exception message including HTTP response data.
+ *
+ * @return The exception message
+ */
+ public String getMessage() {
+ return String.format(java.util.Locale.ROOT, "Message: %s%nHTTP response code: %s%nHTTP response body: %s%nHTTP response headers: %s",
+ super.getMessage(), this.getCode(), this.getResponseBody(), this.getResponseHeaders());
+ }
+ {{#errorObjectType}}
+
+ /**
+ * Get the error object.
+ *
+ * @return Error object
+ */
+ public {{{errorObjectType}}} getErrorObject() {
+ return errorObject;
+ }
+
+ /**
+ * Get the error object.
+ *
+ * @param errorObject Error object
+ */
+ public void setErrorObject({{{errorObjectType}}} errorObject) {
+ this.errorObject = errorObject;
+ }
+ {{/errorObjectType}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_doc.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_doc.mustache
new file mode 100644
index 000000000000..5d349f582593
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_doc.mustache
@@ -0,0 +1,110 @@
+# {{classname}}{{#description}}
+{{.}}{{/description}}
+
+All URIs are relative to *{{basePath}}*
+
+| Method | HTTP request | Description |
+|------------- | ------------- | -------------|
+{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} |
+{{/operation}}{{/operations}}
+
+{{#operations}}
+{{#operation}}
+
+# **{{operationId}}**{{^vendorExtensions.x-group-parameters}}
+> {{#returnType}}{{.}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}
+> {{#returnType}}{{.}} {{/returnType}}{{operationId}}({{#requiredParams}}{{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}.{{paramName}}({{paramName}}){{/optionalParams}}.execute();{{/vendorExtensions.x-group-parameters}}
+
+{{summary}}{{#notes}}
+
+{{.}}{{/notes}}
+
+### Example
+```java
+// Import classes:
+import {{{invokerPackage}}}.ApiClient;
+import {{{invokerPackage}}}.ApiException;
+import {{{invokerPackage}}}.Configuration;{{#hasAuthMethods}}
+import {{{invokerPackage}}}.auth.*;{{/hasAuthMethods}}
+import {{{modelPackage}}}.*;
+import {{{package}}}.{{{classname}}};
+
+public class Example {
+ public static void main(String[] args) {
+ ApiClient defaultClient = Configuration.getDefaultApiClient();
+ defaultClient.setBasePath("{{{basePath}}}");
+ {{#withAWSV4Signature}}
+ // Configure AWS Signature V4 authorization
+ defaultClient.setAWS4Configuration("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY", "REGION", "SERVICE")
+ {{/withAWSV4Signature}}
+ {{#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}}
+ {{/authMethods}}
+ {{/hasAuthMethods}}
+
+ {{{classname}}} apiInstance = new {{{classname}}}(defaultClient);
+ {{#allParams}}
+ {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}}
+ {{/allParams}}
+ try {
+ {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}{{^vendorExtensions.x-group-parameters}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}({{#requiredParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}
+ .{{{paramName}}}({{{paramName}}}){{/optionalParams}}
+ .execute();{{/vendorExtensions.x-group-parameters}}{{#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();
+ }
+ }
+}
+```
+
+### Parameters
+{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
+| Name | Type | Description | Notes |
+|------------- | ------------- | ------------- | -------------|{{/-last}}{{/allParams}}
+{{#allParams}}| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}{{^baseType}}{{#isModel}}{{dataType}}{{/isModel}}{{#isEnumRef}}{{dataType}}{{/isEnumRef}}{{/baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{^isContainer}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{/isContainer}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} |
+{{/allParams}}
+
+### Return type
+
+{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}}
+
+### Authorization
+
+{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}}
+
+### HTTP request headers
+
+ - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
+ - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}}
+
+{{#responses.0}}
+### HTTP response details
+| Status code | Description | Response headers |
+|-------------|-------------|------------------|
+{{#responses}}
+| **{{code}}** | {{message}} | {{#headers}} * {{baseName}} - {{description}}
{{/headers}}{{^headers.0}} - {{/headers.0}} |
+{{/responses}}
+{{/responses.0}}
+
+{{/operation}}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_test.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_test.mustache
new file mode 100644
index 000000000000..b56bdf4db09b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/api_test.mustache
@@ -0,0 +1,65 @@
+{{>licenseInfo}}
+
+package {{package}};
+
+import {{invokerPackage}}.ApiException;
+{{#imports}}import {{import}};
+{{/imports}}
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+{{#supportStreaming}}
+import java.io.InputStream;
+{{/supportStreaming}}
+
+{{#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}}
+ {{#vendorExtensions.x-streaming}}
+ InputStream response = api.{{operationId}}{{^vendorExtensions.x-group-parameters}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}({{#requiredParams}}{{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}
+ .{{paramName}}({{paramName}}){{/optionalParams}}
+ .execute();{{/vendorExtensions.x-group-parameters}}
+ {{/vendorExtensions.x-streaming}}
+ {{^vendorExtensions.x-streaming}}
+ {{#returnType}}{{{returnType}}} response = {{/returnType}}api.{{operationId}}{{^vendorExtensions.x-group-parameters}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}({{#requiredParams}}{{paramName}}{{^-last}}, {{/-last}}{{/requiredParams}}){{#optionalParams}}
+ .{{paramName}}({{paramName}}){{/optionalParams}}
+ .execute();{{/vendorExtensions.x-group-parameters}}
+ {{/vendorExtensions.x-streaming}}
+ // TODO: test validations
+ }
+
+ {{/operation}}
+ {{/operations}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/AWS4Auth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/AWS4Auth.mustache
new file mode 100644
index 000000000000..d74c3db611ea
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/AWS4Auth.mustache
@@ -0,0 +1,114 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ApiException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.signer.Aws4Signer;
+import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
+import software.amazon.awssdk.http.ContentStreamProvider;
+import software.amazon.awssdk.http.SdkHttpFullRequest;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.regions.Region;
+
+import okio.Buffer;
+
+{{>generatedAnnotation}}
+
+public class AWS4Auth implements Authentication {
+
+ private AwsCredentials credentials;
+ private String region;
+ private String service;
+
+ public AWS4Auth() {
+ this.credentials = AnonymousCredentialsProvider.create().resolveCredentials();
+ }
+
+ public void setCredentials(String accessKey, String secretKey) {
+ this.credentials = AwsBasicCredentials.create(accessKey, secretKey);
+ }
+
+ public void setCredentials(String accessKey, String secretKey, String sessionToken) {
+ this.credentials = AwsSessionCredentials.create(accessKey, secretKey, sessionToken);
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams,
+ Map cookieParams, byte[] payload, String method, URI uri)
+ throws ApiException {
+
+ SdkHttpFullRequest.Builder requestBuilder =
+ SdkHttpFullRequest.builder().uri(uri).method(SdkHttpMethod.fromValue(method));
+
+ // SigV4 requires every x-amz-* header that is actually sent to be part of the canonical
+ // request, otherwise AWS rejects the signature. Content-Type is deliberately not signed:
+ // OkHttp's BridgeInterceptor rewrites multipart content types to append the boundary, so the
+ // signed value would not match the value on the wire.
+ for (Map.Entry header : headerParams.entrySet()) {
+ if (header.getKey().toLowerCase(java.util.Locale.ROOT).startsWith("x-amz-")) {
+ requestBuilder.putHeader(header.getKey(), header.getValue());
+ }
+ }
+
+ ContentStreamProvider provider = new ContentStreamProvider() {
+ @Override
+ public InputStream newStream() {
+ return new ByteArrayInputStream(payload);
+ }
+ };
+
+ requestBuilder = requestBuilder.contentStreamProvider(provider);
+
+ SdkHttpFullRequest signableRequest = sign(requestBuilder);
+
+ Map headers = signableRequest.headers().entrySet().stream()
+ .collect(Collectors.toMap(s -> s.getKey(), e -> e.getValue().get(0)));
+
+ headerParams.putAll(headers);
+ }
+
+ /**
+ * AWS Signature Version 4 signing.
+ *
+ * @param request {@link SdkHttpFullRequest.Builder}
+ * @return {@link SdkHttpFullRequest}
+ */
+ private SdkHttpFullRequest sign(final SdkHttpFullRequest.Builder request) {
+
+ SdkHttpFullRequest req = request.build();
+
+ if (this.service != null && this.region != null && this.credentials != null) {
+ Aws4SignerParams params = Aws4SignerParams.builder().signingName(this.service)
+ .signingRegion(Region.of(this.region)).awsCredentials(this.credentials).build();
+
+ Aws4Signer signer = Aws4Signer.create();
+
+ req = signer.sign(req, params);
+ }
+
+ return req;
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/ApiKeyAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/ApiKeyAuth.mustache
new file mode 100644
index 000000000000..31a92bfa5d80
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/ApiKeyAuth.mustache
@@ -0,0 +1,70 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.ApiException;
+import {{invokerPackage}}.Pair;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.List;
+
+{{>generatedAnnotation}}
+
+public class ApiKeyAuth implements Authentication {
+ private final String location;
+ private final String paramName;
+
+ private String apiKey;
+ private String apiKeyPrefix;
+
+ public ApiKeyAuth(String location, String paramName) {
+ this.location = location;
+ this.paramName = paramName;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public String getParamName() {
+ return paramName;
+ }
+
+ public String getApiKey() {
+ return apiKey;
+ }
+
+ public void setApiKey(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ public String getApiKeyPrefix() {
+ return apiKeyPrefix;
+ }
+
+ public void setApiKeyPrefix(String apiKeyPrefix) {
+ this.apiKeyPrefix = apiKeyPrefix;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams, Map cookieParams,
+ byte[] payload, String method, URI uri) throws ApiException {
+ if (apiKey == null) {
+ return;
+ }
+ String value;
+ if (apiKeyPrefix != null) {
+ value = apiKeyPrefix + " " + apiKey;
+ } else {
+ value = apiKey;
+ }
+ if ("query".equals(location)) {
+ queryParams.add(new Pair(paramName, value));
+ } else if ("header".equals(location)) {
+ headerParams.put(paramName, value);
+ } else if ("cookie".equals(location)) {
+ cookieParams.put(paramName, value);
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/Authentication.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/Authentication.mustache
new file mode 100644
index 000000000000..1dee610e63f6
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/Authentication.mustache
@@ -0,0 +1,27 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ApiException;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.List;
+
+{{>generatedAnnotation}}
+
+public interface Authentication {
+ /**
+ * Apply authentication settings to header and query params.
+ *
+ * @param queryParams List of query parameters
+ * @param headerParams Map of header parameters
+ * @param cookieParams Map of cookie parameters
+ * @param payload HTTP request body
+ * @param method HTTP method
+ * @param uri URI
+ * @throws ApiException if failed to update the parameters
+ */
+ void applyToParams(List queryParams, Map headerParams, Map cookieParams, byte[] payload, String method, URI uri) throws ApiException;
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBasicAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBasicAuth.mustache
new file mode 100644
index 000000000000..52e7c07a9be3
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBasicAuth.mustache
@@ -0,0 +1,44 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ApiException;
+
+import okhttp3.Credentials;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.List;
+
+{{>generatedAnnotation}}
+
+public class HttpBasicAuth implements Authentication {
+ private String username;
+ private String password;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams, Map cookieParams,
+ byte[] payload, String method, URI uri) throws ApiException {
+ if (username == null && password == null) {
+ return;
+ }
+ headerParams.put("Authorization", Credentials.basic(username == null ? "" : username, password == null ? "" : password));
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBearerAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBearerAuth.mustache
new file mode 100644
index 000000000000..8008a0de73af
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/HttpBearerAuth.mustache
@@ -0,0 +1,64 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ApiException;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+{{>generatedAnnotation}}
+
+public class HttpBearerAuth implements Authentication {
+ private final String scheme;
+ private Supplier tokenSupplier;
+
+ public HttpBearerAuth(String scheme) {
+ this.scheme = scheme;
+ }
+
+ /**
+ * Gets the token, which together with the scheme, will be sent as the value of the Authorization header.
+ *
+ * @return The bearer token
+ */
+ public String getBearerToken() {
+ return tokenSupplier == null ? null : tokenSupplier.get();
+ }
+
+ /**
+ * Sets the token, which together with the scheme, will be sent as the value of the Authorization header.
+ *
+ * @param bearerToken The bearer token
+ */
+ public void setBearerToken(String bearerToken) {
+ this.tokenSupplier = () -> bearerToken;
+ }
+
+ /**
+ * Sets the supplier of tokens, which together with the scheme, will be sent as the value of the Authorization header.
+ *
+ * @param tokenSupplier The supplier of bearer tokens
+ */
+ public void setBearerToken(Supplier tokenSupplier) {
+ this.tokenSupplier = tokenSupplier;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams, Map cookieParams,
+ byte[] payload, String method, URI uri) throws ApiException {
+ String bearerToken = Optional.ofNullable(tokenSupplier).map(Supplier::get).orElse(null);
+ if (bearerToken == null) {
+ return;
+ }
+ headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken);
+ }
+
+ private static String upperCaseBearer(String scheme) {
+ return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme;
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/OAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/OAuth.mustache
new file mode 100644
index 000000000000..d4032f6ee0f5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/OAuth.mustache
@@ -0,0 +1,33 @@
+{{>licenseInfo}}
+
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.Pair;
+import {{invokerPackage}}.ApiException;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.List;
+
+{{>generatedAnnotation}}
+
+public class OAuth implements Authentication {
+ private String accessToken;
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams, Map cookieParams,
+ byte[] payload, String method, URI uri) throws ApiException {
+ if (accessToken == null) {
+ return;
+ }
+ headerParams.put("Authorization", "Bearer " + accessToken);
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/RetryingOAuth.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/RetryingOAuth.mustache
new file mode 100644
index 000000000000..03e37577957d
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/auth/RetryingOAuth.mustache
@@ -0,0 +1,264 @@
+{{>licenseInfo}}
+
+{{#hasOAuthMethods}}
+package {{invokerPackage}}.auth;
+
+import {{invokerPackage}}.ApiException;
+import {{invokerPackage}}.JSON;
+import {{invokerPackage}}.Pair;
+
+import okhttp3.FormBody;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RetryingOAuth extends OAuth implements Interceptor {
+ private final OkHttpClient client;
+ private final TokenRequestBuilder tokenRequestBuilder;
+ private final JSON json;
+
+ public RetryingOAuth(OkHttpClient client, TokenRequestBuilder tokenRequestBuilder) {
+ this.client = client;
+ this.tokenRequestBuilder = tokenRequestBuilder;
+ this.json = new JSON();
+ }
+
+ public RetryingOAuth(TokenRequestBuilder tokenRequestBuilder) {
+ this(new OkHttpClient(), tokenRequestBuilder);
+ }
+
+ public RetryingOAuth(
+ String tokenUrl,
+ String clientId,
+ OAuthFlow flow,
+ String clientSecret,
+ Map parameters
+ ) {
+ this(TokenRequestBuilder.tokenLocation(tokenUrl)
+ .setClientId(clientId)
+ .setClientSecret(clientSecret));
+ setFlow(flow);
+ if (parameters != null) {
+ for (Map.Entry entry : parameters.entrySet()) {
+ tokenRequestBuilder.setParameter(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ public void setFlow(OAuthFlow flow) {
+ switch(flow) {
+ case ACCESS_CODE:
+ tokenRequestBuilder.setGrantType("authorization_code");
+ break;
+ case IMPLICIT:
+ tokenRequestBuilder.setGrantType("implicit");
+ break;
+ case PASSWORD:
+ tokenRequestBuilder.setGrantType("password");
+ break;
+ case APPLICATION:
+ tokenRequestBuilder.setGrantType("client_credentials");
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ return retryingIntercept(chain, true);
+ }
+
+ private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException {
+ Request request = chain.request();
+
+ // If the request already has an authorization (e.g. Basic auth), proceed with the request as is
+ if (request.header("Authorization") != null) {
+ return chain.proceed(request);
+ }
+
+ // Get the token if it has not yet been acquired
+ if (getAccessToken() == null) {
+ updateAccessToken(null);
+ }
+
+ if (getAccessToken() != null) {
+ // Build the request
+ Request.Builder requestBuilder = request.newBuilder();
+ requestBuilder.header("Authorization", "Bearer " + getAccessToken());
+
+ // Execute the request
+ Response response = chain.proceed(requestBuilder.build());
+
+ // 401/403 response codes most likely indicate an expired access token, unless it happens two times in a row
+ if (
+ response != null &&
+ ( response.code() == HttpURLConnection.HTTP_UNAUTHORIZED ||
+ response.code() == HttpURLConnection.HTTP_FORBIDDEN ) &&
+ updateTokenAndRetryOnAuthorizationFailure
+ ) {
+ try {
+ String requestAccessToken = getAccessToken();
+ if (updateAccessToken(requestAccessToken)) {
+ if (response.body() != null) {
+ response.body().close();
+ }
+ return retryingIntercept(chain, false);
+ }
+ } catch (Exception e) {
+ if (response.body() != null) {
+ response.body().close();
+ }
+ throw e;
+ }
+ }
+ return response;
+ }
+ else {
+ return chain.proceed(chain.request());
+ }
+ }
+
+ public synchronized boolean updateAccessToken(String requestAccessToken) throws IOException {
+ if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) {
+ Request tokenRequest = tokenRequestBuilder.build();
+ try (Response response = client.newCall(tokenRequest).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ AccessTokenResponse tokenResponse = json.deserialize(response.body().string(), AccessTokenResponse.class);
+ if (tokenResponse != null && tokenResponse.accessToken != null) {
+ setAccessToken(tokenResponse.accessToken);
+ }
+ }
+ }
+ }
+ return getAccessToken() == null || !getAccessToken().equals(requestAccessToken);
+ }
+
+ public TokenRequestBuilder getTokenRequestBuilder() {
+ return tokenRequestBuilder;
+ }
+
+ @Override
+ public void applyToParams(List queryParams, Map headerParams, Map cookieParams,
+ byte[] payload, String method, URI uri) throws ApiException {
+ // No implementation necessary
+ }
+
+ public static class AccessTokenResponse {
+ {{#isJackson}}
+ @com.fasterxml.jackson.annotation.JsonProperty("access_token")
+ {{/isJackson}}
+ {{#isGson}}
+ @com.google.gson.annotations.SerializedName("access_token")
+ {{/isGson}}
+ {{#isJsonb}}
+ @jakarta.json.bind.annotation.JsonbProperty("access_token")
+ {{/isJsonb}}
+ public String accessToken;
+
+ {{#isJackson}}
+ @com.fasterxml.jackson.annotation.JsonProperty("token_type")
+ {{/isJackson}}
+ {{#isGson}}
+ @com.google.gson.annotations.SerializedName("token_type")
+ {{/isGson}}
+ {{#isJsonb}}
+ @jakarta.json.bind.annotation.JsonbProperty("token_type")
+ {{/isJsonb}}
+ public String tokenType;
+
+ {{#isJackson}}
+ @com.fasterxml.jackson.annotation.JsonProperty("expires_in")
+ {{/isJackson}}
+ {{#isGson}}
+ @com.google.gson.annotations.SerializedName("expires_in")
+ {{/isGson}}
+ {{#isJsonb}}
+ @jakarta.json.bind.annotation.JsonbProperty("expires_in")
+ {{/isJsonb}}
+ public Long expiresIn;
+
+ {{#isJackson}}
+ @com.fasterxml.jackson.annotation.JsonProperty("refresh_token")
+ {{/isJackson}}
+ {{#isGson}}
+ @com.google.gson.annotations.SerializedName("refresh_token")
+ {{/isGson}}
+ {{#isJsonb}}
+ @jakarta.json.bind.annotation.JsonbProperty("refresh_token")
+ {{/isJsonb}}
+ public String refreshToken;
+
+ {{#isJackson}}
+ @com.fasterxml.jackson.annotation.JsonProperty("scope")
+ {{/isJackson}}
+ {{#isGson}}
+ @com.google.gson.annotations.SerializedName("scope")
+ {{/isGson}}
+ {{#isJsonb}}
+ @jakarta.json.bind.annotation.JsonbProperty("scope")
+ {{/isJsonb}}
+ public String scope;
+ }
+
+ public static class TokenRequestBuilder {
+ private String tokenUrl;
+ private String clientId;
+ private String clientSecret;
+ private String grantType;
+ private final Map parameters = new HashMap<>();
+
+ public static TokenRequestBuilder tokenLocation(String tokenUrl) {
+ return new TokenRequestBuilder().setTokenUrl(tokenUrl);
+ }
+
+ public TokenRequestBuilder setTokenUrl(String tokenUrl) {
+ this.tokenUrl = tokenUrl;
+ return this;
+ }
+
+ public TokenRequestBuilder setClientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ public TokenRequestBuilder setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ return this;
+ }
+
+ public TokenRequestBuilder setGrantType(String grantType) {
+ this.grantType = grantType;
+ return this;
+ }
+
+ public TokenRequestBuilder setParameter(String name, String value) {
+ this.parameters.put(name, value);
+ return this;
+ }
+
+ public Request build() {
+ FormBody.Builder formBuilder = new FormBody.Builder();
+ if (grantType != null) formBuilder.add("grant_type", grantType);
+ if (clientId != null) formBuilder.add("client_id", clientId);
+ if (clientSecret != null) formBuilder.add("client_secret", clientSecret);
+ for (Map.Entry entry : parameters.entrySet()) {
+ formBuilder.add(entry.getKey(), entry.getValue());
+ }
+
+ return new Request.Builder()
+ .url(tokenUrl)
+ .post(formBuilder.build())
+ .build();
+ }
+ }
+}
+{{/hasOAuthMethods}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.gradle.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.gradle.mustache
new file mode 100644
index 000000000000..4b91e47271d9
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.gradle.mustache
@@ -0,0 +1,156 @@
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+apply plugin: 'java'
+apply plugin: 'maven-publish'
+
+group = '{{groupId}}'
+version = '{{artifactVersion}}'
+
+repositories {
+ mavenCentral()
+}
+{{#sourceFolder}}
+sourceSets {
+ main.java.srcDirs = ['{{sourceFolder}}']
+}
+
+{{/sourceFolder}}
+
+sourceCompatibility = {{#useJackson3}}JavaVersion.VERSION_17{{/useJackson3}}{{^useJackson3}}{{#java17}}JavaVersion.VERSION_17{{/java17}}{{^java17}}{{#java11}}JavaVersion.VERSION_11{{/java11}}{{^java11}}JavaVersion.VERSION_1_8{{/java11}}{{/java17}}{{/useJackson3}}
+targetCompatibility = {{#useJackson3}}JavaVersion.VERSION_17{{/useJackson3}}{{^useJackson3}}{{#java17}}JavaVersion.VERSION_17{{/java17}}{{^java17}}{{#java11}}JavaVersion.VERSION_11{{/java11}}{{^java11}}JavaVersion.VERSION_1_8{{/java11}}{{/java17}}{{/useJackson3}}
+
+publishing {
+ publications {
+ maven(MavenPublication) {
+ artifactId = '{{artifactId}}'
+ from components.java
+ }
+ }
+}
+
+task execute(type:JavaExec) {
+ mainClass = System.getProperty('mainClass')
+ classpath = sourceSets.main.runtimeClasspath
+}
+
+ext {
+ {{#swagger1AnnotationLibrary}}
+ swagger_annotations_version = "1.6.6"
+ {{/swagger1AnnotationLibrary}}
+ {{#swagger2AnnotationLibrary}}
+ swagger_annotations_version = "2.2.15"
+ {{/swagger2AnnotationLibrary}}
+ {{#useJakartaEe}}
+ jakarta_annotation_version = "2.1.1"
+ {{#useBeanValidation}}
+ bean_validation_version = "3.0.2"
+ {{/useBeanValidation}}
+ {{/useJakartaEe}}
+ {{^useJakartaEe}}
+ jakarta_annotation_version = "1.3.5"
+ {{#useBeanValidation}}
+ bean_validation_version = "2.0.2"
+ {{/useBeanValidation}}
+ {{/useJakartaEe}}
+ {{#isJackson}}
+ {{#useJackson3}}
+ jackson_version = "3.2.1"
+ {{/useJackson3}}
+ {{^useJackson3}}
+ jackson_version = "2.22.1"
+ {{/useJackson3}}
+ jackson_annotations_version = "2.22"
+ {{/isJackson}}
+ okhttp_version = "5.4.0"
+}
+
+dependencies {
+ {{#swagger1AnnotationLibrary}}
+ implementation "io.swagger:swagger-annotations:$swagger_annotations_version"
+ {{/swagger1AnnotationLibrary}}
+ {{#swagger2AnnotationLibrary}}
+ implementation "io.swagger.core.v3:swagger-annotations:$swagger_annotations_version"
+ {{/swagger2AnnotationLibrary}}
+ {{^useJspecify}}
+ implementation "com.google.code.findbugs:jsr305:3.0.2"
+ {{/useJspecify}}
+ {{#useJspecify}}
+ implementation "org.jspecify:jspecify:1.0.0"
+ {{/useJspecify}}
+ implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
+ implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
+ {{#isGson}}
+ implementation 'com.google.code.gson:gson:2.10.1'
+ implementation 'io.gsonfire:gson-fire:1.9.0'
+ {{/isGson}}
+ {{#isJackson}}
+ implementation "{{jacksonPackage}}.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_annotations_version"
+ implementation "{{jacksonPackage}}.core:jackson-databind:$jackson_version"
+ {{^useJackson3}}
+ implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
+ {{/useJackson3}}
+ {{/isJackson}}
+ {{#openApiNullable}}
+ {{^isJsonb}}
+ implementation 'org.openapitools:jackson-databind-nullable:0.2.11'
+ {{/isJsonb}}
+ {{/openApiNullable}}
+ {{#isJsonb}}
+ implementation 'jakarta.json.bind:jakarta.json.bind-api:3.0.1'
+ implementation 'org.eclipse:yasson:3.0.4'
+ implementation 'jakarta.json:jakarta.json-api:2.1.3'
+ implementation 'org.eclipse.parsson:parsson:1.1.7'
+ {{/isJsonb}}
+ {{#withAWSV4Signature}}
+ implementation 'software.amazon.awssdk:auth:2.20.157'
+ {{/withAWSV4Signature}}
+ {{#useReflectionEqualsHashCode}}
+ implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.18.0'
+ {{/useReflectionEqualsHashCode}}
+ {{#joda}}
+ implementation 'joda-time:joda-time:2.12.0'
+ {{#isJackson}}
+ implementation "{{jacksonPackage}}.datatype:jackson-datatype-joda:$jackson_version"
+ {{/isJackson}}
+ {{/joda}}
+ {{#dynamicOperations}}
+ implementation 'io.swagger.parser.v3:swagger-parser-v3:2.0.30'
+ {{/dynamicOperations}}
+ {{#parcelableModel}}
+ // Needed for Parcelable support
+ compileOnly 'com.google.android:android:4.1.1.4'
+ {{/parcelableModel}}
+ compileOnly "jakarta.annotation:jakarta.annotation-api:$jakarta_annotation_version"
+ {{#useBeanValidation}}
+ compileOnly "jakarta.validation:jakarta.validation-api:$bean_validation_version"
+ {{/useBeanValidation}}
+ {{#performBeanValidation}}
+ // Bean Validation Impl. used to perform BeanValidation
+ {{#useJakartaEe}}
+ implementation 'org.hibernate.validator:hibernate-validator:8.0.3.Final'
+ // Jakarta EL API + implementation, required by Hibernate Validator to interpolate constraint messages
+ implementation 'jakarta.el:jakarta.el-api:5.0.1'
+ implementation 'org.glassfish.expressly:expressly:5.0.0'
+ {{/useJakartaEe}}
+ {{^useJakartaEe}}
+ implementation 'org.hibernate.validator:hibernate-validator:6.2.5.Final'
+ // javax.el API + implementation, required by Hibernate Validator to interpolate constraint messages
+ implementation 'org.glassfish:jakarta.el:3.0.4'
+ {{/useJakartaEe}}
+ {{/performBeanValidation}}
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.3'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.3'
+}
+
+javadoc {
+ options.tags = [ "http.response.details:a:Http Response Details" ]
+}
+
+test {
+ useJUnitPlatform()
+ dependsOn 'cleanTest'
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.sbt.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.sbt.mustache
new file mode 100644
index 000000000000..2380daa4b729
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/build.sbt.mustache
@@ -0,0 +1,88 @@
+// {{#useJackson3}}Jackson 3 requires Java 17.{{/useJackson3}}{{^useJackson3}}Keep the bytecode level in sync with the Maven and Gradle builds.{{/useJackson3}}
+val javaVersion = "{{#useJackson3}}17{{/useJackson3}}{{^useJackson3}}{{#java17}}17{{/java17}}{{^java17}}{{#java11}}11{{/java11}}{{^java11}}1.8{{/java11}}{{/java17}}{{/useJackson3}}"
+
+lazy val root = (project in file(".")).
+ settings(
+ organization := "{{groupId}}",
+ name := "{{artifactId}}",
+ version := "{{artifactVersion}}",
+ scalaVersion := "2.13.6",
+ scalacOptions ++= Seq("-feature"),
+ compile / javacOptions ++= Seq("-Xlint:deprecation", "-source", javaVersion, "-target", javaVersion),
+ Compile / packageDoc / publishArtifact := false,
+ resolvers += Resolver.mavenLocal,
+ libraryDependencies ++= Seq(
+ {{#swagger1AnnotationLibrary}}
+ "io.swagger" % "swagger-annotations" % "1.6.6",
+ {{/swagger1AnnotationLibrary}}
+ {{#swagger2AnnotationLibrary}}
+ "io.swagger.core.v3" % "swagger-annotations" % "2.2.15",
+ {{/swagger2AnnotationLibrary}}
+ {{^useJspecify}}
+ "com.google.code.findbugs" % "jsr305" % "3.0.2",
+ {{/useJspecify}}
+ {{#useJspecify}}
+ "org.jspecify" % "jspecify" % "1.0.0",
+ {{/useJspecify}}
+ "com.squareup.okhttp3" % "okhttp" % "5.4.0",
+ "com.squareup.okhttp3" % "logging-interceptor" % "5.4.0",
+ {{#isGson}}
+ "com.google.code.gson" % "gson" % "2.10.1",
+ "io.gsonfire" % "gson-fire" % "1.9.0",
+ {{/isGson}}
+ {{#isJackson}}
+ "{{jacksonPackage}}.core" % "jackson-core" % "{{#useJackson3}}3.2.1{{/useJackson3}}{{^useJackson3}}2.22.1{{/useJackson3}}",
+ "com.fasterxml.jackson.core" % "jackson-annotations" % "2.22",
+ "{{jacksonPackage}}.core" % "jackson-databind" % "{{#useJackson3}}3.2.1{{/useJackson3}}{{^useJackson3}}2.22.1{{/useJackson3}}",
+ {{^useJackson3}}
+ "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.22.1",
+ {{/useJackson3}}
+ {{/isJackson}}
+ {{#openApiNullable}}
+ {{^isJsonb}}
+ "org.openapitools" % "jackson-databind-nullable" % "0.2.11",
+ {{/isJsonb}}
+ {{/openApiNullable}}
+ {{#isJsonb}}
+ "jakarta.json.bind" % "jakarta.json.bind-api" % "3.0.1",
+ "org.eclipse" % "yasson" % "3.0.4",
+ "jakarta.json" % "jakarta.json-api" % "2.1.3",
+ "org.eclipse.parsson" % "parsson" % "1.1.7",
+ {{/isJsonb}}
+ {{#useReflectionEqualsHashCode}}
+ "org.apache.commons" % "commons-lang3" % "3.18.0",
+ {{/useReflectionEqualsHashCode}}
+ {{#joda}}
+ "joda-time" % "joda-time" % "2.12.0",
+ {{#isJackson}}
+ "{{jacksonPackage}}.datatype" % "jackson-datatype-joda" % "{{#useJackson3}}3.2.1{{/useJackson3}}{{^useJackson3}}2.22.1{{/useJackson3}}",
+ {{/isJackson}}
+ {{/joda}}
+ "jakarta.annotation" % "jakarta.annotation-api" % "{{#useJakartaEe}}2.1.1{{/useJakartaEe}}{{^useJakartaEe}}1.3.5{{/useJakartaEe}}",
+ {{#withAWSV4Signature}}
+ "software.amazon.awssdk" % "auth" % "2.20.157",
+ {{/withAWSV4Signature}}
+ {{#dynamicOperations}}
+ "io.swagger.parser.v3" % "swagger-parser-v3" % "2.0.30",
+ {{/dynamicOperations}}
+ {{#parcelableModel}}
+ "com.google.android" % "android" % "4.1.1.4" % "provided",
+ {{/parcelableModel}}
+ {{#useBeanValidation}}
+ "jakarta.validation" % "jakarta.validation-api" % "{{#useJakartaEe}}3.0.2{{/useJakartaEe}}{{^useJakartaEe}}2.0.2{{/useJakartaEe}}",
+ {{/useBeanValidation}}
+ {{#performBeanValidation}}
+ {{#useJakartaEe}}
+ "org.hibernate.validator" % "hibernate-validator" % "8.0.3.Final",
+ "jakarta.el" % "jakarta.el-api" % "5.0.1",
+ "org.glassfish.expressly" % "expressly" % "5.0.0",
+ {{/useJakartaEe}}
+ {{^useJakartaEe}}
+ "org.hibernate.validator" % "hibernate-validator" % "6.2.5.Final",
+ "org.glassfish" % "jakarta.el" % "3.0.4",
+ {{/useJakartaEe}}
+ {{/performBeanValidation}}
+ "org.junit.jupiter" % "junit-jupiter-api" % "5.10.3" % "test",
+ "com.novocode" % "junit-interface" % "0.10" % "test"
+ )
+ )
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/javaBuilder.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/javaBuilder.mustache
new file mode 100644
index 000000000000..5f46db09575f
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/javaBuilder.mustache
@@ -0,0 +1,99 @@
+public static class Builder {{#parentModel}}extends {{classname}}.Builder {{/parentModel}}{
+
+ private {{classname}} instance;
+
+ public Builder() {
+ this(new {{classname}}());
+ }
+
+ protected Builder({{classname}} instance) {
+ {{#parentModel}}
+ super(instance);
+ {{/parentModel}}
+ this.instance = instance;
+ }
+
+ {{#vars}}
+ public {{classname}}.Builder {{name}}({{>nullableArgument_builder}} {{name}}) {
+ {{#vendorExtensions.x-is-jackson-optional-nullable}}
+ this.instance.{{name}} = JsonNullable.<{{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}}>of({{name}});
+ {{/vendorExtensions.x-is-jackson-optional-nullable}}
+ {{^vendorExtensions.x-is-jackson-optional-nullable}}
+ this.instance.{{name}} = {{name}};
+ {{/vendorExtensions.x-is-jackson-optional-nullable}}
+ return this;
+ }
+ {{#vendorExtensions.x-is-jackson-optional-nullable}}
+ public {{classname}}.Builder {{name}}(JsonNullable<{{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}}> {{name}}) {
+ this.instance.{{name}} = {{name}};
+ return this;
+ }
+ {{/vendorExtensions.x-is-jackson-optional-nullable}}
+ {{/vars}}
+
+{{#parentVars}}
+ public {{classname}}.Builder {{name}}({{>nullableArgument_builder}} {{name}}) { // inherited: {{isInherited}}
+ super.{{name}}({{name}});
+ return this;
+ }
+ {{#vendorExtensions.x-is-jackson-optional-nullable}}
+ public {{classname}}.Builder {{name}}(JsonNullable<{{#removeAnnotations}}{{{datatypeWithEnum}}}{{/removeAnnotations}}> {{name}}) {
+ this.instance.{{name}} = {{name}};
+ return this;
+ }
+ {{/vendorExtensions.x-is-jackson-optional-nullable}}
+
+ {{/parentVars}}
+
+{{#isAdditionalPropertiesTrue}}
+ /**
+ * Copies the additional (undeclared) properties into the instance under construction.
+ *
+ * The values are put through {@link {{classname}}#putAdditionalProperty}, so the map is
+ * rebuilt on the instance that actually owns it: a subclass declares its own holder that
+ * shadows the parent one, and the virtual call always reaches the subclass field.
+ */
+ public {{classname}}.Builder additionalProperties(Map additionalProperties) {
+ if (additionalProperties != null) {
+ additionalProperties.forEach(this.instance::putAdditionalProperty);
+ }
+ return this;
+ }
+
+{{/isAdditionalPropertiesTrue}}
+ /**
+ * returns a built {{classname}} instance.
+ *
+ * The builder is not reusable.
+ */
+ public {{classname}} build() {
+ try {
+ return this.instance;
+ } finally {
+ // ensure that this.instance is not reused{{#parentModel}}
+ super.build();{{/parentModel}}
+ this.instance = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass() + "=(" + instance + ")";
+ }
+ }
+
+ /**
+ * Create a builder with no initialized field.
+ */
+ public static {{classname}}.Builder builder() {
+ return new {{classname}}.Builder();
+ }
+
+ /**
+ * Create a builder with a shallow copy of this instance.
+ */
+ public {{classname}}.Builder toBuilder() {
+ return new {{classname}}.Builder(){{#allVars}}
+ .{{name}}({{getter}}()){{/allVars}}{{#isAdditionalPropertiesTrue}}
+ .additionalProperties(getAdditionalProperties()){{/isAdditionalPropertiesTrue}};
+ }
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model.mustache
new file mode 100644
index 000000000000..213e83ab2bad
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model.mustache
@@ -0,0 +1,35 @@
+{{>licenseInfo}}
+
+package {{package}};
+
+{{#useReflectionEqualsHashCode}}
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+{{/useReflectionEqualsHashCode}}
+import java.util.Objects;
+{{#imports}}
+import {{import}};
+{{/imports}}
+{{#serializableModel}}
+import java.io.Serializable;
+{{/serializableModel}}
+{{#withXml}}
+import {{javaxPackage}}.xml.bind.annotation.*;
+{{/withXml}}
+{{#parcelableModel}}
+import android.os.Parcelable;
+import android.os.Parcel;
+{{/parcelableModel}}
+{{#useBeanValidation}}
+import {{javaxPackage}}.validation.constraints.*;
+import {{javaxPackage}}.validation.Valid;
+{{/useBeanValidation}}
+{{#performBeanValidation}}
+import org.hibernate.validator.constraints.*;
+{{/performBeanValidation}}
+
+{{#models}}
+{{#model}}
+{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#vendorExtensions.x-is-one-of-interface}}{{>oneof_interface}}{{/vendorExtensions.x-is-one-of-interface}}{{^vendorExtensions.x-is-one-of-interface}}{{#oneOf}}{{#-first}}{{>oneof_model}}{{/-first}}{{/oneOf}}{{^oneOf}}{{#anyOf}}{{#-first}}{{>anyof_model}}{{/-first}}{{/anyOf}}{{^anyOf}}{{>pojo}}{{/anyOf}}{{/oneOf}}{{/vendorExtensions.x-is-one-of-interface}}{{/isEnum}}
+{{/model}}
+{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelEnum.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelEnum.mustache
new file mode 100644
index 000000000000..6bdfd5e5df9b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelEnum.mustache
@@ -0,0 +1,125 @@
+import java.io.IOException;
+{{#isUri}}
+import java.net.URI;
+{{/isUri}}
+{{#isGson}}
+import com.google.gson.TypeAdapter;
+import com.google.gson.JsonElement;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+{{/isGson}}
+{{#isJackson}}
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+{{/isJackson}}
+{{#isJsonb}}
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+{{/isJsonb}}
+
+/**
+ * {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
+ */
+{{#isDeprecated}}
+@Deprecated
+{{/isDeprecated}}
+{{#isGson}}
+@JsonAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Adapter.class)
+{{/isGson}}
+{{#isJsonb}}{{! JSON-B has no @JsonValue equivalent; without an adapter the constant name would be
+ written instead of the wire value. }}
+@JsonbTypeAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Adapter.class)
+{{/isJsonb}}
+{{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {
+ {{#allowableValues}}{{#enumVars}}
+ {{#enumDescription}}
+ /**
+ * {{.}}
+ */
+ {{/enumDescription}}
+ {{#withXml}}
+ @XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}})
+ {{/withXml}}
+ {{{name}}}({{{value}}}){{^-last}},
+ {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}}
+
+ private {{{dataType}}} value;
+
+ {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) {
+ this.value = value;
+ }
+
+ {{#isJackson}}
+ @JsonValue
+ {{/isJackson}}
+ public {{{dataType}}} getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ {{#isJackson}}
+ @JsonCreator
+ {{/isJackson}}
+ public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) {
+ for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
+ if (b.value.equals{{#isString}}{{#useEnumCaseInsensitive}}IgnoreCase{{/useEnumCaseInsensitive}}{{/isString}}(value)) {
+ return b;
+ }
+ }
+ {{#isNullable}}return null;{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}{{#allowableValues}}{{#enumVars}}{{#-last}}return {{{name}}};{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/enumUnknownDefaultCase}}{{/isNullable}}
+ }
+
+ {{#isGson}}
+ public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> {
+ @Override
+ public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumeration) throws IOException {
+ jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}});
+ }
+
+ @Override
+ public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException {
+ {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}}{{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
+ return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new java.math.BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
+ }
+ }
+
+ public static void validateJsonElement(JsonElement jsonElement) throws IOException {
+ {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isUri}}URI.create({{/isUri}}jsonElement.{{#isNumber}}getAsString(){{/isNumber}}{{#isInteger}}getAsInt(){{/isInteger}}{{#isUri}}getAsString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}getAs{{{dataType}}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
+ {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new java.math.BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
+ }
+ {{/isGson}}
+ {{#isJsonb}}
+ public static class Adapter implements JsonbAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}, {{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}}> {
+ @Override
+ public {{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}} adaptToJson({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumeration) {
+ return enumeration == null ? null : enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}};
+ }
+
+ @Override
+ public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} adaptFromJson({{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}} value) {
+ return value == null ? null : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.fromValue({{#isUri}}URI.create(value){{/isUri}}{{^isUri}}value{{/isUri}});
+ }
+ }
+ {{/isJsonb}}
+{{#supportUrlQuery}}
+
+ /**
+ * Convert the instance into URL query string.
+ *
+ * @param prefix prefix of the query string
+ * @return URL query string
+ */
+ public String toUrlQueryString(String prefix) {
+ if (prefix == null) {
+ prefix = "";
+ }
+
+ return String.format(java.util.Locale.ROOT, "%s=%s", prefix, this.toString());
+ }
+{{/supportUrlQuery}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelInnerEnum.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelInnerEnum.mustache
new file mode 100644
index 000000000000..607aec303af0
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/modelInnerEnum.mustache
@@ -0,0 +1,93 @@
+ /**
+ * {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
+ */
+ {{#isGson}}
+ @JsonAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.Adapter.class)
+ {{/isGson}}
+ {{#isJsonb}}{{! JSON-B has no @JsonValue equivalent; without an adapter the constant name would be
+ written instead of the wire value. }}
+ @JsonbTypeAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.Adapter.class)
+ {{/isJsonb}}
+{{#withXml}}
+ @XmlType(name="{{datatypeWithEnum}}")
+ @XmlEnum({{dataType}}.class)
+{{/withXml}}
+ {{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {
+ {{#allowableValues}}
+ {{#enumVars}}
+ {{#enumDescription}}
+ /**
+ * {{.}}
+ */
+ {{/enumDescription}}
+ {{#withXml}}
+ @XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}})
+ {{/withXml}}
+ {{{name}}}({{{value}}}){{^-last}},
+ {{/-last}}{{#-last}};{{/-last}}
+ {{/enumVars}}
+ {{/allowableValues}}
+
+ private {{{dataType}}} value;
+
+ {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{dataType}}} value) {
+ this.value = value;
+ }
+
+ {{#isJackson}}
+ @JsonValue
+ {{/isJackson}}
+ public {{{dataType}}} getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ {{#isJackson}}
+ @JsonCreator
+ {{/isJackson}}
+ public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) {
+ for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
+ if (b.value.equals{{#isString}}{{#useEnumCaseInsensitive}}IgnoreCase{{/useEnumCaseInsensitive}}{{/isString}}(value)) {
+ return b;
+ }
+ }
+ {{#isNullable}}return null;{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}{{#allowableValues}}{{#enumVars}}{{#-last}}return {{{name}}};{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/enumUnknownDefaultCase}}{{/isNullable}}
+ }
+
+ {{#isGson}}
+ public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}> {
+ @Override
+ public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} enumeration) throws IOException {
+ jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}});
+ }
+
+ @Override
+ public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException {
+ {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}} {{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
+ return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new java.math.BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
+ }
+ }
+
+ public static void validateJsonElement(JsonElement jsonElement) throws IOException {
+ {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isUri}}URI.create({{/isUri}}jsonElement.{{#isNumber}}getAsString(){{/isNumber}}{{#isInteger}}getAsInt(){{/isInteger}}{{#isUri}}getAsString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}getAs{{{dataType}}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
+ {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new java.math.BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
+ }
+ {{/isGson}}
+ {{#isJsonb}}
+ public static class Adapter implements JsonbAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}, {{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}}> {
+ @Override
+ public {{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}} adaptToJson({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} enumeration) {
+ return enumeration == null ? null : enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}};
+ }
+
+ @Override
+ public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} adaptFromJson({{#isUri}}String{{/isUri}}{{^isUri}}{{{dataType}}}{{/isUri}} value) {
+ return value == null ? null : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isUri}}URI.create(value){{/isUri}}{{^isUri}}value{{/isUri}});
+ }
+ }
+ {{/isJsonb}}
+ }
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model_test.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model_test.mustache
new file mode 100644
index 000000000000..eb1643edf8cc
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/model_test.mustache
@@ -0,0 +1,43 @@
+{{>licenseInfo}}
+
+package {{package}};
+
+{{! The model's import list is serialization-library specific and nothing below uses it: the test bodies
+ are stubs and the model itself is in this package. Echoing it here pulled Jackson annotations into
+ Gson and JSON-B builds, which do not have them on the classpath. }}
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Model tests for {{classname}}
+ */
+public class {{classname}}Test {
+ {{#models}}
+ {{#model}}
+ {{^vendorExtensions.x-is-one-of-interface}}
+ {{^isEnum}}
+ private final {{classname}} model = new {{classname}}();
+
+ {{/isEnum}}
+ /**
+ * Model tests for {{classname}}
+ */
+ @Test
+ public void test{{classname}}() {
+ // TODO: test {{classname}}
+ }
+
+ {{#allVars}}
+ /**
+ * Test the property '{{name}}'
+ */
+ @Test
+ public void {{name}}Test() {
+ // TODO: test {{name}}
+ }
+
+ {{/allVars}}
+ {{/vendorExtensions.x-is-one-of-interface}}
+ {{/model}}
+ {{/models}}
+}
diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/oneof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/oneof_model.mustache
new file mode 100644
index 000000000000..6ee1ff700702
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp/oneof_model.mustache
@@ -0,0 +1,1319 @@
+{{#isGson}}
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.JsonPrimitive;
+{{! TypeAdapter, JsonAdapter, SerializedName, JsonReader and JsonWriter are contributed via
+ model.imports by JavaClientCodegen; emitting them here as well produced duplicates. }}
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonArray;
+{{/isGson}}
+{{#isJackson}}
+{{#isAdditionalPropertiesTrue}}
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+{{/isAdditionalPropertiesTrue}}
+import {{jacksonPackage}}.core.JsonGenerator;
+import {{jacksonPackage}}.core.JsonParser;
+import {{jacksonPackage}}.core.JsonToken;
+import {{jacksonPackage}}.core.type.TypeReference;
+import {{jacksonPackage}}.databind.DeserializationContext;
+{{^useJackson3}}
+import {{jacksonPackage}}.databind.JsonMappingException;
+{{/useJackson3}}
+import {{jacksonPackage}}.databind.JsonNode;
+import {{jacksonPackage}}.databind.MapperFeature;
+{{^useJackson3}}
+import {{jacksonPackage}}.databind.SerializerProvider;
+{{/useJackson3}}
+import {{jacksonPackage}}.databind.annotation.JsonDeserialize;
+import {{jacksonPackage}}.databind.annotation.JsonSerialize;
+import {{jacksonPackage}}.databind.deser.std.StdDeserializer;
+import {{jacksonPackage}}.databind.ser.std.StdSerializer;
+import {{jacksonPackage}}.databind.JavaType;
+{{#useJackson3}}
+import {{jacksonPackage}}.core.JacksonException;
+import {{jacksonPackage}}.databind.DatabindException;
+import {{jacksonPackage}}.databind.SerializationContext;
+{{/useJackson3}}
+{{/isJackson}}
+
+{{#isJsonb}}
+{{! additional_properties.mustache annotates its holder field with @JsonbTransient, so the
+ import has to be here too - a pojo gets it from pojo.mustache, a composed model does not. }}
+import jakarta.json.bind.annotation.JsonbTransient;
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+{{/isJsonb}}
+{{! java.io.IOException, java.util.ArrayList, java.util.HashMap, java.util.List and
+ java.util.Map are contributed via model.imports by JavaClientCodegen.fromModel, which is
+ also where codegen imports them for container properties. Emitting them here as well
+ produced duplicate import lines. The rest have no Java importMapping entry and are not
+ duplicated, so they stay. }}
+import java.lang.reflect.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.Collections;
+import java.util.HashSet;
+{{! java.util.Objects is already imported by model.mustache for every model it renders. }}
+import java.util.StringJoiner;
+
+import {{invokerPackage}}.JSON;
+
+{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}}
+{{#isJackson}}
+@JsonDeserialize(using = {{classname}}.{{classname}}Deserializer.class)
+@JsonSerialize(using = {{classname}}.{{classname}}Serializer.class)
+{{/isJackson}}
+{{#isJsonb}}
+@JsonbTypeDeserializer({{classname}}.{{classname}}Deserializer.class)
+@JsonbTypeSerializer({{classname}}.{{classname}}Serializer.class)
+{{/isJsonb}}
+public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} {
+ private static final Logger log = Logger.getLogger({{classname}}.class.getName());
+
+{{>additional_properties}}
+ {{#isAdditionalPropertiesTrue}}
+ {{#isGson}}
+ /**
+ * Record the properties that the matched oneOf schema did not consume.
+ *
+ * The wrapper only owns what the selected child schema left behind. When that child accepts
+ * additional properties itself it absorbs them all and this records nothing, so a property is
+ * never stored — and therefore never written — twice.
+ */
+ private static void collectUnconsumedProperties({{classname}} instance, JsonElement jsonElement, Gson gson) {
+ if (jsonElement == null || !jsonElement.isJsonObject() || instance.getActualInstance() == null) {
+ return;
+ }
+ JsonElement consumedElement = gson.toJsonTree(instance.getActualInstance());
+ if (consumedElement == null || !consumedElement.isJsonObject()) {
+ return;
+ }
+ JsonObject consumed = consumedElement.getAsJsonObject();
+ for (Map.Entry entry : jsonElement.getAsJsonObject().entrySet()) {
+ if (consumed.has(entry.getKey())) {
+ continue;
+ }
+ JsonElement value = entry.getValue();
+ if (value.isJsonPrimitive()) {
+ JsonPrimitive primitive = value.getAsJsonPrimitive();
+ if (primitive.isString()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsString());
+ } else if (primitive.isNumber()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsNumber());
+ } else if (primitive.isBoolean()) {
+ instance.putAdditionalProperty(entry.getKey(), primitive.getAsBoolean());
+ }
+ } else if (value.isJsonArray()) {
+ instance.putAdditionalProperty(entry.getKey(), gson.fromJson(value, List.class));
+ } else if (value.isJsonObject()) {
+ instance.putAdditionalProperty(entry.getKey(), gson.fromJson(value, HashMap.class));
+ }
+ }
+ }
+
+ /**
+ * Merge the unconsumed properties back into the JSON produced by the matched oneOf schema.
+ */
+ private static void writeUnconsumedProperties({{classname}} value, JsonObject obj, Gson gson) {
+ if (value.getAdditionalProperties() == null) {
+ return;
+ }
+ for (Map.Entry entry : value.getAdditionalProperties().entrySet()) {
+ if (obj.has(entry.getKey())) {
+ // the child schema owns this property
+ continue;
+ }
+ obj.add(entry.getKey(), gson.toJsonTree(entry.getValue()));
+ }
+ }
+ {{/isGson}}
+ {{#isJackson}}
+ /**
+ * Record the properties that the matched oneOf schema did not consume.
+ *
+ * The wrapper only owns what the selected child schema left behind. When that child accepts
+ * additional properties itself it absorbs them all and this records nothing, so a property is
+ * never stored — and therefore never written — twice.
+ */
+ private static void collectUnconsumedProperties({{classname}} instance, JsonNode tree) {
+ if (tree == null || !tree.isObject() || instance.getActualInstance() == null) {
+ return;
+ }
+ JsonNode consumed = JSON.getMapper().valueToTree(instance.getActualInstance());
+ if (consumed == null || !consumed.isObject()) {
+ return;
+ }
+ java.util.Iterator> fields = tree.{{^useJackson3}}fields{{/useJackson3}}{{#useJackson3}}properties{{/useJackson3}}(){{#useJackson3}}.iterator(){{/useJackson3}};
+ while (fields.hasNext()) {
+ Map.Entry