com.datadog.api.client.ApiClient Maven / Gradle / Ivy
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/
package com.datadog.api.client;
import com.datadog.api.client.auth.ApiKeyAuth;
import com.datadog.api.client.auth.Authentication;
import com.datadog.api.client.auth.HttpBasicAuth;
import com.datadog.api.client.auth.HttpBearerAuth;
import com.datadog.api.client.auth.OAuth;
import jakarta.ws.rs.client.AsyncInvoker;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.InvocationCallback;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Form;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.Variant;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.filter.EncodingFilter;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.Boundary;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.message.DeflateEncoder;
import org.glassfish.jersey.message.GZipEncoder;
@jakarta.annotation.Generated(
value = "https://github.com/DataDog/datadog-api-client-java/blob/master/.generator")
public class ApiClient {
protected Map defaultHeaderMap = new HashMap();
protected Map defaultCookieMap = new HashMap();
protected String basePath = "https://api.datadoghq.com";
protected String userAgent;
private DateTimeFormatter offsetDateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
protected List servers =
new ArrayList(
Arrays.asList(
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"The regional site for Datadog customers.",
"datadoghq.com",
new HashSet(
Arrays.asList(
"datadoghq.com",
"us3.datadoghq.com",
"us5.datadoghq.com",
"ap1.datadoghq.com",
"datadoghq.eu",
"ddog-gov.com"))));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"api",
new HashSet()));
}
}),
new ServerConfiguration(
"{protocol}://{name}",
"No description provided",
new HashMap() {
{
put(
"name",
new ServerVariable(
"Full site DNS name.", "api.datadoghq.com", new HashSet()));
put(
"protocol",
new ServerVariable(
"The protocol for accessing the API.",
"https",
new HashSet()));
}
}),
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"Any Datadog deployment.", "datadoghq.com", new HashSet()));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"api",
new HashSet()));
}
})));
protected Integer serverIndex = 0;
protected Map serverVariables = null;
protected Map> operationServers =
new HashMap>() {
{
put(
"v1.IpRangesApi.getIPRanges",
new ArrayList(
Arrays.asList(
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"The regional site for Datadog customers.",
"datadoghq.com",
new HashSet(
Arrays.asList(
"datadoghq.com",
"us3.datadoghq.com",
"us5.datadoghq.com",
"ap1.datadoghq.com",
"datadoghq.eu",
"ddog-gov.com"))));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"ip-ranges",
new HashSet()));
}
}),
new ServerConfiguration(
"{protocol}://{name}",
"No description provided",
new HashMap() {
{
put(
"name",
new ServerVariable(
"Full site DNS name.",
"ip-ranges.datadoghq.com",
new HashSet()));
put(
"protocol",
new ServerVariable(
"The protocol for accessing the API.",
"https",
new HashSet()));
}
}),
new ServerConfiguration(
"https://{subdomain}.datadoghq.com",
"No description provided",
new HashMap() {
{
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"ip-ranges",
new HashSet()));
}
}))));
put(
"v1.LogsApi.submitLog",
new ArrayList(
Arrays.asList(
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"The regional site for Datadog customers.",
"datadoghq.com",
new HashSet(
Arrays.asList(
"datadoghq.com",
"us3.datadoghq.com",
"us5.datadoghq.com",
"ap1.datadoghq.com",
"datadoghq.eu",
"ddog-gov.com"))));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"http-intake.logs",
new HashSet()));
}
}),
new ServerConfiguration(
"{protocol}://{name}",
"No description provided",
new HashMap() {
{
put(
"name",
new ServerVariable(
"Full site DNS name.",
"http-intake.logs.datadoghq.com",
new HashSet()));
put(
"protocol",
new ServerVariable(
"The protocol for accessing the API.",
"https",
new HashSet()));
}
}),
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"Any Datadog deployment.",
"datadoghq.com",
new HashSet()));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"http-intake.logs",
new HashSet()));
}
}))));
put(
"v2.LogsApi.submitLog",
new ArrayList(
Arrays.asList(
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"The regional site for customers.",
"datadoghq.com",
new HashSet(
Arrays.asList(
"datadoghq.com",
"us3.datadoghq.com",
"us5.datadoghq.com",
"ap1.datadoghq.com",
"datadoghq.eu",
"ddog-gov.com"))));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"http-intake.logs",
new HashSet()));
}
}),
new ServerConfiguration(
"{protocol}://{name}",
"No description provided",
new HashMap() {
{
put(
"name",
new ServerVariable(
"Full site DNS name.",
"http-intake.logs.datadoghq.com",
new HashSet()));
put(
"protocol",
new ServerVariable(
"The protocol for accessing the API.",
"https",
new HashSet()));
}
}),
new ServerConfiguration(
"https://{subdomain}.{site}",
"No description provided",
new HashMap() {
{
put(
"site",
new ServerVariable(
"Any Datadog deployment.",
"datadoghq.com",
new HashSet()));
put(
"subdomain",
new ServerVariable(
"The subdomain where the API is deployed.",
"http-intake.logs",
new HashSet()));
}
}))));
}
};
protected Map operationServerIndex = new HashMap();
protected Map> operationServerVariables =
new HashMap>();
protected boolean debugging = false;
protected RetryConfig retry = new RetryConfig(false, 2, 2, 3);
protected boolean compress = true;
protected ClientConfig clientConfig;
protected int connectionTimeout = 0;
private int readTimeout = 0;
protected Client httpClient;
protected JSON json;
protected String tempFolderPath = null;
protected Map authentications;
protected DateFormat dateFormat;
protected final Map unstableOperations =
new HashMap() {
{
put("v2.createOpenAPI", false);
put("v2.deleteOpenAPI", false);
put("v2.getOpenAPI", false);
put("v2.listAPIs", false);
put("v2.updateOpenAPI", false);
put("v2.createApp", false);
put("v2.deleteApp", false);
put("v2.deleteApps", false);
put("v2.getApp", false);
put("v2.listApps", false);
put("v2.updateApp", false);
put("v2.deployApp", false);
put("v2.disableApp", false);
put("v2.getActiveBillingDimensions", false);
put("v2.getBillingDimensionMapping", false);
put("v2.getMonthlyCostAttribution", false);
put("v2.cancelDataDeletionRequest", false);
put("v2.createDataDeletionRequest", false);
put("v2.getDataDeletionRequests", false);
put("v2.createDORADeployment", false);
put("v2.createDORAIncident", false);
put("v2.createIncident", false);
put("v2.createIncidentIntegration", false);
put("v2.createIncidentTodo", false);
put("v2.createIncidentType", false);
put("v2.deleteIncident", false);
put("v2.deleteIncidentIntegration", false);
put("v2.deleteIncidentTodo", false);
put("v2.deleteIncidentType", false);
put("v2.getIncident", false);
put("v2.getIncidentIntegration", false);
put("v2.getIncidentTodo", false);
put("v2.getIncidentType", false);
put("v2.listIncidentAttachments", false);
put("v2.listIncidentIntegrations", false);
put("v2.listIncidents", false);
put("v2.listIncidentTodos", false);
put("v2.listIncidentTypes", false);
put("v2.searchIncidents", false);
put("v2.updateIncident", false);
put("v2.updateIncidentAttachments", false);
put("v2.updateIncidentIntegration", false);
put("v2.updateIncidentTodo", false);
put("v2.updateIncidentType", false);
put("v2.createAWSAccount", false);
put("v2.createNewAWSExternalID", false);
put("v2.deleteAWSAccount", false);
put("v2.getAWSAccount", false);
put("v2.listAWSAccounts", false);
put("v2.listAWSNamespaces", false);
put("v2.updateAWSAccount", false);
put("v2.listAWSLogsServices", false);
put("v2.cancelHistoricalJob", false);
put("v2.convertJobResultToSignal", false);
put("v2.deleteHistoricalJob", false);
put("v2.getFinding", false);
put("v2.getHistoricalJob", false);
put("v2.listFindings", false);
put("v2.listHistoricalJobs", false);
put("v2.muteFindings", false);
put("v2.runHistoricalJob", false);
put("v2.createScorecardOutcomesBatch", false);
put("v2.createScorecardRule", false);
put("v2.deleteScorecardRule", false);
put("v2.listScorecardOutcomes", false);
put("v2.listScorecardRules", false);
put("v2.updateScorecardRule", false);
put("v2.createIncidentService", false);
put("v2.deleteIncidentService", false);
put("v2.getIncidentService", false);
put("v2.listIncidentServices", false);
put("v2.updateIncidentService", false);
put("v2.createSLOReportJob", false);
put("v2.getSLOReport", false);
put("v2.getSLOReportJobStatus", false);
put("v2.createIncidentTeam", false);
put("v2.deleteIncidentTeam", false);
put("v2.getIncidentTeam", false);
put("v2.listIncidentTeams", false);
put("v2.updateIncidentTeam", false);
}
};
protected static final java.util.logging.Logger logger =
java.util.logging.Logger.getLogger(ApiClient.class.getName());
private static ApiClient defaultApiClient;
/**
* Get the default API client, which would be used when creating API instances without providing
* an API client.
*
* @return Default API client
*/
public static ApiClient getDefaultApiClient() {
if (defaultApiClient != null) {
return defaultApiClient;
}
defaultApiClient = new ApiClient();
// Configure the Datadog site to send API calls to
String site = System.getenv("DD_SITE");
if (site != null) {
HashMap serverVariables = new HashMap();
serverVariables.put("site", site);
defaultApiClient.setServerVariables(serverVariables);
}
// Configure API key authorization
HashMap secrets = new HashMap();
String apiKeyAuth = System.getenv("DD_API_KEY");
if (apiKeyAuth != null) {
secrets.put("apiKeyAuth", apiKeyAuth);
}
String appKeyAuth = System.getenv("DD_APP_KEY");
if (appKeyAuth != null) {
secrets.put("appKeyAuth", appKeyAuth);
}
defaultApiClient.configureApiKeys(secrets);
return defaultApiClient;
}
/**
* Set the default API client, which would be used when creating API instances without providing
* an API client.
*
* @param apiClient API client
*/
public static void setDefaultApiClient(ApiClient apiClient) {
defaultApiClient = apiClient;
}
/** Constructs a new ApiClient with default parameters. */
public ApiClient() {
this(null);
}
/**
* Constructs a new ApiClient with the specified authentication parameters.
*
* @param authMap A hash map containing authentication parameters.
*/
public ApiClient(Map authMap) {
json = new JSON();
httpClient = buildHttpClient();
this.dateFormat = new RFC3339DateFormat();
// Set default User-Agent.
setUserAgent();
// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap();
Authentication auth = null;
if (authMap != null) {
auth = authMap.get("AuthZ");
}
if (auth instanceof OAuth) {
authentications.put("AuthZ", auth);
} else {
authentications.put("AuthZ", new OAuth(basePath, "/oauth2/v1/token"));
}
if (authMap != null) {
auth = authMap.get("apiKeyAuth");
}
if (auth instanceof ApiKeyAuth) {
authentications.put("apiKeyAuth", auth);
} else {
authentications.put("apiKeyAuth", new ApiKeyAuth("header", "DD-API-KEY"));
}
if (authMap != null) {
auth = authMap.get("appKeyAuth");
}
if (auth instanceof ApiKeyAuth) {
authentications.put("appKeyAuth", auth);
} else {
authentications.put("appKeyAuth", new ApiKeyAuth("header", "DD-APPLICATION-KEY"));
}
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
}
/**
* Get the date format used to parse/format {@code OffsetDateTime} parameters.
*
* @return DateTimeFormatter
*/
public DateTimeFormatter getOffsetDateTimeFormatter() {
return offsetDateTimeFormatter;
}
/**
* Add custom retry object in the client
*
* @param retry retry object
*/
public void setRetry(RetryConfig retry) {
this.retry = retry;
}
/**
* Return the retryConfig object
*
* @return retryConfig
*/
public RetryConfig getRetry() {
return retry;
}
/**
* Enable retry directly on the client instead of creating a new retry object
*
* @param enableRetry bool, enable retry or not
*/
public void enableRetry(boolean enableRetry) {
this.retry.setEnableRetry(enableRetry);
}
/**
* Set the date format used to parse/format {@code OffsetDateTime} parameters.
*
* @param offsetDateTimeFormatter {@code DateTimeFormatter}
*/
public void setOffsetDateTimeFormatter(DateTimeFormatter offsetDateTimeFormatter) {
this.offsetDateTimeFormatter = offsetDateTimeFormatter;
}
/**
* Format the given {@code OffsetDateTime} object into string.
*
* @param offsetDateTime {@code OffsetDateTime}
* @return {@code OffsetDateTime} in string format
*/
public String formatOffsetDateTime(OffsetDateTime offsetDateTime) {
return offsetDateTimeFormatter.format(offsetDateTime);
}
/**
* Gets the JSON instance to do JSON serialization and deserialization.
*
* @return JSON
*/
public JSON getJSON() {
return json;
}
public Client getHttpClient() {
return httpClient;
}
public ApiClient setHttpClient(Client httpClient) {
this.httpClient = httpClient;
return this;
}
/**
* Returns the base URL to the location where the OpenAPI document is being served.
*
* @return The base URL to the target host.
*/
public String getBasePath() {
return basePath;
}
/**
* Sets the base URL to the location where the OpenAPI document is being served.
*
* @param basePath The base URL to the target host.
* @return API client
*/
public ApiClient setBasePath(String basePath) {
this.basePath = basePath;
setOauthBasePath(basePath);
return this;
}
public List getServers() {
return servers;
}
public ApiClient setServers(List servers) {
this.servers = servers;
updateBasePath();
return this;
}
public Integer getServerIndex() {
return serverIndex;
}
public ApiClient setServerIndex(Integer serverIndex) {
this.serverIndex = serverIndex;
updateBasePath();
return this;
}
public Map getServerVariables() {
return serverVariables;
}
public ApiClient setServerVariables(Map serverVariables) {
this.serverVariables = serverVariables;
updateBasePath();
return this;
}
private void updateBasePath() {
if (serverIndex != null) {
setBasePath(servers.get(serverIndex).URL(serverVariables));
}
}
private void setOauthBasePath(String basePath) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setBasePath(basePath);
}
}
}
/**
* Get authentications (key: authentication name, value: authentication).
*
* @return Map of authentication object
*/
public Map getAuthentications() {
return authentications;
}
/**
* Get authentication for the given name.
*
* @param authName The authentication name
* @return The authentication, null if not found
*/
public Authentication getAuthentication(String authName) {
return authentications.get(authName);
}
/**
* Helper method to set username for the first HTTP basic authentication.
*
* @param username Username
* @return API client
*/
public ApiClient setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return this;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set password for the first HTTP basic authentication.
*
* @param password Password
* @return API client
*/
public ApiClient setPassword(String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setPassword(password);
return this;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set API key value for the first API key authentication.
*
* @param apiKey API key
* @return API client
*/
public ApiClient setApiKey(String apiKey) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKey(apiKey);
return this;
}
}
throw new RuntimeException("No API key authentication configured!");
}
/**
* Helper method to configure authentications which respects aliases of API keys.
*
* @param secrets Hash map from authentication name to its secret.
* @return API client
*/
public ApiClient configureApiKeys(Map secrets) {
for (Map.Entry authEntry : authentications.entrySet()) {
Authentication auth = authEntry.getValue();
if (auth instanceof ApiKeyAuth) {
String name = authEntry.getKey();
if (secrets.containsKey(name)) {
((ApiKeyAuth) auth).setApiKey(secrets.get(name));
}
}
}
return this;
}
/**
* Helper method to set API key prefix for the first API key authentication.
*
* @param apiKeyPrefix API key prefix
* @return API client
*/
public ApiClient setApiKeyPrefix(String apiKeyPrefix) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
return this;
}
}
throw new RuntimeException("No API key authentication configured!");
}
/**
* Helper method to set bearer token for the first Bearer authentication.
*
* @param bearerToken Bearer token
* @return API client
*/
public ApiClient setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return this;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}
/**
* Helper method to set access token for the first OAuth2 authentication.
*
* @param accessToken Access token
* @return API client
*/
public ApiClient setAccessToken(String accessToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setAccessToken(accessToken);
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Helper method to set the credentials for the first OAuth2 authentication.
*
* @param clientId the client ID
* @param clientSecret the client secret
* @return API client
*/
public ApiClient setOauthCredentials(String clientId, String clientSecret) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setCredentials(clientId, clientSecret, isDebugging());
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Helper method to set the password flow for the first OAuth2 authentication.
*
* @param username the user name
* @param password the user password
* @return API client
*/
public ApiClient setOauthPasswordFlow(String username, String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).usePasswordFlow(username, password);
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Helper method to set the authorization code flow for the first OAuth2 authentication.
*
* @param code the authorization code
* @return API client
*/
public ApiClient setOauthAuthorizationCodeFlow(String code) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).useAuthorizationCodeFlow(code);
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Helper method to set the scopes for the first OAuth2 authentication.
*
* @param scope the oauth scope
* @return API client
*/
public ApiClient setOauthScope(String scope) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setScope(scope);
return this;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
/**
* Set the User-Agent header's value (by adding to the default header map).
*
* @param userAgent Http user agent
* @return API client
*/
public ApiClient setUserAgent(String userAgent) {
addDefaultHeader("User-Agent", userAgent);
return this;
}
/**
* Get the User-Agent header's value.
*
* @return User-Agent string
*/
public String getUserAgent() {
return userAgent;
}
/**
* Set the default User-Agent header's value with telemetry information (by adding to the default
* header map).
*
* @return API client
*/
public ApiClient setUserAgent() {
final Properties properties = new Properties();
try {
properties.load(
getClass().getClassLoader().getResourceAsStream("com/datadog/api/project.properties"));
} catch (IOException e) {
logger.severe("Could not load client version: " + e.toString());
}
String userAgent =
"datadog-api-client-java/"
+ properties.getProperty("version")
+ " ("
+ "java "
+ System.getProperty("java.version")
+ "; "
+ "java_vendor "
+ System.getProperty("java.vendor")
+ "; "
+ "os "
+ System.getProperty("os.name")
+ "; "
+ "os_version "
+ System.getProperty("os.version")
+ "; "
+ "arch "
+ System.getProperty("os.arch")
+ ")";
addDefaultHeader("User-Agent", userAgent);
this.userAgent = userAgent;
return this;
}
/**
* Add a default header.
*
* @param key The header's key
* @param value The header's value
* @return API client
*/
public ApiClient addDefaultHeader(String key, String value) {
defaultHeaderMap.put(key, value);
return this;
}
/**
* Add a default cookie.
*
* @param key The cookie's key
* @param value The cookie's value
* @return API client
*/
public ApiClient addDefaultCookie(String key, String value) {
defaultCookieMap.put(key, value);
return this;
}
/**
* Gets the client config.
*
* @return Client config
*/
public ClientConfig getClientConfig() {
return clientConfig;
}
/**
* Set the client config.
*
* @param clientConfig Set the client config
* @return API client
*/
public ApiClient setClientConfig(ClientConfig clientConfig) {
this.clientConfig = clientConfig;
// Rebuild HTTP Client according to the new "clientConfig" value.
this.httpClient = buildHttpClient();
return this;
}
/**
* Check that whether debugging is enabled for this API client.
*
* @return True if debugging is switched on
*/
public boolean isDebugging() {
return debugging;
}
/**
* Enable/disable debugging for this API client.
*
* @param debugging To enable (true) or disable (false) debugging
* @return API client
*/
public ApiClient setDebugging(boolean debugging) {
this.debugging = debugging;
// Rebuild HTTP Client according to the new "debugging" value.
this.setClientConfig(null);
return this;
}
/**
* Check that whether compress is enabled for this API client.
*
* @return True if compress is switched on
*/
public boolean isCompress() {
return compress;
}
/**
* Enable/disable compress for this API client.
*
* @param compress To enable (true) or disable (false) compress
* @return API client
*/
public ApiClient setCompress(boolean compress) {
this.compress = compress;
// Rebuild HTTP Client according to the new "compress" value.
this.setClientConfig(null);
return this;
}
/**
* The path of temporary folder used to store downloaded files from endpoints with file response.
* The default value is null
, i.e. using the system's default temporary folder.
*
* @return Temp folder path
*/
public String getTempFolderPath() {
return tempFolderPath;
}
/**
* Set temp folder path
*
* @param tempFolderPath Temp folder path
* @return API client
*/
public ApiClient setTempFolderPath(String tempFolderPath) {
this.tempFolderPath = tempFolderPath;
return this;
}
/**
* Connect timeout (in milliseconds).
*
* @return Connection timeout
*/
public int getConnectTimeout() {
return connectionTimeout;
}
/**
* Set the connect timeout (in milliseconds). A value of 0 means no timeout, otherwise values must
* be between 1 and {@link Integer#MAX_VALUE}.
*
* @param connectionTimeout Connection timeout in milliseconds
* @return API client
*/
public ApiClient setConnectTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
httpClient.property(ClientProperties.CONNECT_TIMEOUT, connectionTimeout);
return this;
}
/**
* read timeout (in milliseconds).
*
* @return Read timeout
*/
public int getReadTimeout() {
return readTimeout;
}
/**
* Set the read timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be
* between 1 and {@link Integer#MAX_VALUE}.
*
* @param readTimeout Read timeout in milliseconds
* @return API client
*/
public ApiClient setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
httpClient.property(ClientProperties.READ_TIMEOUT, readTimeout);
return this;
}
/**
* Get the date format used to parse/format date parameters.
*
* @return Date format
*/
public DateFormat getDateFormat() {
return dateFormat;
}
/**
* Set the date format used to parse/format date parameters.
*
* @param dateFormat Date format
* @return API client
*/
public ApiClient setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
// also set the date format for model (de)serialization with Date properties
this.json.setDateFormat((DateFormat) dateFormat.clone());
return this;
}
/**
* Get list of all unstable operations
*
* @return set of all unstable operations Ids
*/
public Set getUnstableOperations() {
return unstableOperations.keySet();
}
/**
* Mark an unstable operation as enabled/disabled.
*
* @param operation operation Id - this is the name of the method on the API class, e.g.
* "createFoo"
* @param enabled whether to mark the operation as enabled (true) or disabled (false)
* @return true if the operation is marked as unstable and thus was enabled/disabled, false
* otherwise
*/
public boolean setUnstableOperationEnabled(String operation, boolean enabled) {
if (unstableOperations.containsKey(operation)) {
unstableOperations.put(operation, enabled);
return true;
}
logger.warning(
String.format("'%s' is not an unstable operation, can't enable/disable", operation));
return false;
}
/**
* Determine whether an operation is an unstable operation.
*
* @param operation operation Id - this is the name of the method on the API class, e.g.
* "createFoo"
* @return true if the operation is an unstable operation, false otherwise
*/
public boolean isUnstableOperation(String operation) {
return unstableOperations.containsKey(operation);
}
/**
* Determine whether an unstable operation is enabled.
*
* @param operation operation Id - this is the name of the method on the API class, e.g.
* "createFoo"
* @return true if the operation is unstable and it is enabled, false otherwise
*/
public boolean isUnstableOperationEnabled(String operation) {
if (unstableOperations.containsKey(operation)) {
return unstableOperations.get(operation);
} else {
logger.warning(
String.format("'%s' is not an unstable operation, is always enabled", operation));
return true;
}
}
/**
* Get the ApiClient logger
*
* @return ApiClient logger
*/
public java.util.logging.Logger getLogger() {
return logger;
}
/**
* Format the given Date object into string.
*
* @param date Date
* @return Date in string format
*/
public String formatDate(Date date) {
return dateFormat.format(date);
}
/**
* Format the given parameter object into string.
*
* @param param Object
* @return Object in string format
*/
public String parameterToString(Object param) {
if (param == null) {
return "";
} else if (param instanceof Date) {
return formatDate((Date) param);
} else if (param instanceof OffsetDateTime) {
return formatOffsetDateTime((OffsetDateTime) param);
} else if (param instanceof Collection) {
StringBuilder b = new StringBuilder();
for (Object o : (Collection) param) {
if (b.length() > 0) {
b.append(',');
}
b.append(String.valueOf(o));
}
return b.toString();
} else {
return String.valueOf(param);
}
}
/*
* Format to {@code Pair} objects.
* @param collectionFormat Collection format
* @param name Name
* @param value Value
* @return List of pairs
*/
public List parameterToPairs(String collectionFormat, String name, Object value) {
List params = new ArrayList();
// preconditions
if (name == null || name.isEmpty() || value == null) {
return params;
}
Collection> valueCollection;
if (value instanceof Collection) {
valueCollection = (Collection) value;
} else {
params.add(new Pair(name, parameterToString(value)));
return params;
}
if (valueCollection.isEmpty()) {
return params;
}
// get the collection format (default: csv)
String format =
(collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat);
// create the params based on the collection format
if ("multi".equals(format)) {
for (Object item : valueCollection) {
params.add(new Pair(name, parameterToString(item)));
}
return params;
}
String delimiter = ",";
if ("csv".equals(format)) {
delimiter = ",";
} else if ("ssv".equals(format)) {
delimiter = " ";
} else if ("tsv".equals(format)) {
delimiter = "\t";
} else if ("pipes".equals(format)) {
delimiter = "|";
}
StringBuilder sb = new StringBuilder();
for (Object item : valueCollection) {
sb.append(delimiter);
sb.append(parameterToString(item));
}
params.add(new Pair(name, sb.substring(1)));
return params;
}
/**
* Check if the given MIME is a JSON MIME. JSON MIME examples: application/json application/json;
* charset=UTF8 APPLICATION/JSON application/vnd.company+json "* / *" is also default to JSON
*
* @param mime MIME
* @return True if the MIME type is JSON
*/
public boolean isJsonMime(String mime) {
String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
return mime != null && (mime.matches(jsonMime) || "*/*".equals(mime));
}
/**
* Select the Accept header's value from the given accepts array: if JSON exists in the given
* array, use it; otherwise use all of them (joining into a string)
*
* @param accepts The accepts array to select from
* @return The Accept header to use. If the given array is empty, null will be returned (not to
* set the Accept header explicitly).
*/
public String selectHeaderAccept(String[] accepts) {
if (accepts.length == 0) {
return null;
}
for (String accept : accepts) {
if (isJsonMime(accept)) {
return accept;
}
}
return StringUtil.join(accepts, ",");
}
/**
* Select the Content-Type header's value from the given array: if JSON exists in the given array,
* use it; otherwise use the first one of the array.
*
* @param contentTypes The Content-Type array to select from
* @return The Content-Type header to use. If the given array is empty, JSON will be used.
*/
public String selectHeaderContentType(String[] contentTypes) {
if (contentTypes.length == 0) {
return "application/json";
}
for (String contentType : contentTypes) {
if (isJsonMime(contentType)) {
return contentType;
}
}
return contentTypes[0];
}
/**
* Escape the given string to be used as URL query value.
*
* @param str String
* @return Escaped string
*/
public String escapeString(String str) {
try {
return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
return str;
}
}
/**
* Serialize the given Java object into string entity according the given Content-Type (only JSON
* is supported for now).
*
* @param obj Object
* @param formParams Form parameters
* @param contentType Content type header
* @param contentEncoding Content encoding header
* @param isBodyNullable Whether the body can be null or not
* @return Entity
*/
public Entity> serialize(
Object obj,
Map formParams,
String contentType,
String contentEncoding,
boolean isBodyNullable) {
Entity> entity;
Variant variant = new Variant(MediaType.valueOf(contentType), "", contentEncoding);
if (contentType.startsWith("multipart/form-data")) {
MultiPart multiPart = new MultiPart();
for (Entry param : formParams.entrySet()) {
if (param.getValue() instanceof File) {
File file = (File) param.getValue();
FormDataContentDisposition contentDisp =
FormDataContentDisposition.name(param.getKey())
.fileName(file.getName())
.size(file.length())
.build();
multiPart.bodyPart(
new FormDataBodyPart(contentDisp, file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
} else {
FormDataContentDisposition contentDisp =
FormDataContentDisposition.name(param.getKey()).build();
multiPart.bodyPart(
new FormDataBodyPart(contentDisp, parameterToString(param.getValue())));
}
}
MediaType mediaDataType = MediaType.MULTIPART_FORM_DATA_TYPE;
mediaDataType = Boundary.addBoundary(mediaDataType);
entity = Entity.entity(multiPart, mediaDataType);
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
Form form = new Form();
for (Entry param : formParams.entrySet()) {
form.param(param.getKey(), parameterToString(param.getValue()));
}
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE);
} else {
// We let jersey handle the serialization
if (isBodyNullable) { // payload is nullable
if (obj instanceof String) {
entity =
Entity.entity(
obj == null
? "null"
: "\""
+ ((String) obj).replaceAll("\"", Matcher.quoteReplacement("\\\""))
+ "\"",
variant);
} else {
entity = Entity.entity(obj == null ? "null" : obj, variant);
}
} else {
if (obj instanceof String) {
entity =
Entity.entity(
obj == null
? ""
: "\""
+ ((String) obj).replaceAll("\"", Matcher.quoteReplacement("\\\""))
+ "\"",
variant);
} else {
entity = Entity.entity(obj == null ? "" : obj, variant);
}
}
}
return entity;
}
/**
* Deserialize response body to Java object according to the Content-Type.
*
* @param Type
* @param response Response
* @param returnType Return type
* @return Deserialize object
*/
@SuppressWarnings("unchecked")
public T deserialize(Response response, GenericType returnType) {
if (response == null || returnType == null) {
return null;
}
if ("byte[]".equals(returnType.toString())) {
// Handle binary response (byte array).
return (T) response.readEntity(byte[].class);
}
// read the entity stream multiple times
response.bufferEntity();
return response.readEntity(returnType);
}
/**
* Create builder to invoke the API.
*
* @param operation The qualified name of the operation
* @param path The sub-path of the HTTP URL
* @param queryParams The query parameters
* @param headerParams The header parameters
* @param cookieParams The cookie parameters
* @param accepts The list of possible request's Accept header
* @param authNames The authentications to apply
* @return The invocation builder
* @throws ApiException API exception
*/
public Invocation.Builder createBuilder(
String operation,
String path,
List queryParams,
Map headerParams,
Map cookieParams,
String[] accepts,
String[] authNames)
throws ApiException {
// Not using `.target(targetURL).path(path)` below,
// to support (constant) query string in `path`, e.g. "/posts?draft=1"
String targetURL;
if (serverIndex != null && operationServers.containsKey(operation)) {
Integer index =
operationServerIndex.containsKey(operation)
? operationServerIndex.get(operation)
: serverIndex;
Map variables =
operationServerVariables.containsKey(operation)
? operationServerVariables.get(operation)
: serverVariables;
List serverConfigurations = operationServers.get(operation);
if (index < 0 || index >= serverConfigurations.size()) {
throw new ArrayIndexOutOfBoundsException(
String.format(
"Invalid index %d when selecting the host settings. Must be less than %d",
index, serverConfigurations.size()));
}
targetURL = serverConfigurations.get(index).URL(variables) + path;
} else {
targetURL = this.basePath + path;
}
URI parsedURI;
try {
parsedURI = new URI(targetURL);
} catch (URISyntaxException e) {
throw new ApiException(e);
}
WebTarget target = httpClient.target(parsedURI);
for (Pair queryParam : queryParams) {
if (queryParam.getValue() != null) {
target = target.queryParam(queryParam.getName(), escapeString(queryParam.getValue()));
}
}
Invocation.Builder invocationBuilder = target.request().accept(selectHeaderAccept(accepts));
for (Entry entry : cookieParams.entrySet()) {
String value = entry.getValue();
if (value != null) {
invocationBuilder = invocationBuilder.cookie(entry.getKey(), value);
}
}
for (Entry entry : defaultCookieMap.entrySet()) {
String value = entry.getValue();
if (value != null) {
invocationBuilder = invocationBuilder.cookie(entry.getKey(), value);
}
}
// put all headers in one place
Map allHeaderParams = new HashMap<>(defaultHeaderMap);
allHeaderParams.putAll(headerParams);
// update different parameters (e.g. headers) for authentication
updateParamsForAuth(authNames, queryParams, allHeaderParams, cookieParams, target.getUri());
for (Entry entry : allHeaderParams.entrySet()) {
String value = entry.getValue();
if (value != null) {
invocationBuilder = invocationBuilder.header(entry.getKey(), value);
}
}
return invocationBuilder;
}
/**
* Invoke API by sending HTTP request with the given options.
*
* @param Type
* @param invocationBuilder HTTP requests builder
* @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE"
* @param body The request body object
* @param headerParams The header parameters
* @param formParams The form parameters
* @param contentTypes The list of request Content-Type headers
* @param returnType The return type into which to deserialize the response
* @param isBodyNullable True if the body is nullable
* @return The response body in type of string
* @throws ApiException API exception
*/
public ApiResponse invokeAPI(
String method,
Invocation.Builder invocationBuilder,
Map headerParams,
String[] contentTypes,
Object body,
Map formParams,
Boolean isBodyNullable,
GenericType returnType)
throws ApiException {
String contentEncoding = headerParams.get(HttpHeaders.CONTENT_ENCODING);
Entity> entity =
serialize(
body,
formParams,
selectHeaderContentType(contentTypes),
contentEncoding,
isBodyNullable);
Response response = null;
try {
int currentRetry = 0;
while (true) {
response = sendRequest(method, invocationBuilder, entity);
int statusCode = response.getStatusInfo().getStatusCode();
Map> responseHeaders = buildResponseHeaders(response);
if (response.getStatusInfo() == Status.NO_CONTENT) {
return new ApiResponse(statusCode, responseHeaders);
} else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) {
if (returnType == null) {
return new ApiResponse(statusCode, responseHeaders);
} else {
return new ApiResponse(
statusCode, responseHeaders, deserialize(response, returnType));
}
} else if (shouldRetry(currentRetry, statusCode, retry)) {
// Close the response before retry to avoid leaks
try {
response.close();
} catch (Exception e) {
// it's not critical, since the response object is local in method invokeAPI; that's
// fine,
// just continue
}
retry.sleepInterval(calculateRetryInterval(responseHeaders, retry, currentRetry));
currentRetry++;
} else {
String message = "error";
String respBody = null;
if (response.hasEntity()) {
try {
respBody = String.valueOf(response.readEntity(String.class));
message = respBody;
} catch (RuntimeException e) {
// e.printStackTrace();
}
}
throw new ApiException(
response.getStatus(), message, buildResponseHeaders(response), respBody);
}
}
} finally {
try {
response.close();
} catch (Exception e) {
// it's not critical, since the response object is local in method invokeAPI; that's fine,
// just continue
}
}
}
private boolean shouldRetry(int retryCount, int statusCode, RetryConfig retryConfig) {
boolean statusToRetry = false;
if (statusCode == 429 || statusCode >= 500) {
statusToRetry = true;
}
return (retryConfig.maxRetries > retryCount && statusToRetry && retryConfig.isEnableRetry());
}
private int calculateRetryInterval(
Map> responseHeaders, RetryConfig retryConfig, int retryCount) {
if (responseHeaders.get("x-ratelimit-reset") != null) {
List rateLimitHeader = responseHeaders.get("x-ratelimit-reset");
return Integer.parseInt(rateLimitHeader.get(0));
} else {
int retryInterval =
(int) Math.pow(retry.backOffMultiplier, retryCount) * retryConfig.backOffBase;
if (getConnectTimeout() > 0) {
retryInterval = Math.min(retryInterval, getConnectTimeout());
}
return retryInterval;
}
}
private Response sendRequest(
String method, Invocation.Builder invocationBuilder, Entity> entity) {
Response response;
if ("POST".equals(method)) {
response = invocationBuilder.post(entity);
} else if ("PUT".equals(method)) {
response = invocationBuilder.put(entity);
} else if ("DELETE".equals(method)) {
response = invocationBuilder.method("DELETE", entity);
} else if ("PATCH".equals(method)) {
response = invocationBuilder.method("PATCH", entity);
} else {
response = invocationBuilder.method(method);
}
return response;
}
/**
* Invoke API by sending HTTP request with the given options, asynchronously.
*
* @param Type
* @param invocationBuilder HTTP requests builder
* @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE"
* @param body The request body object
* @param headerParams The header parameters
* @param formParams The form parameters
* @param contentTypes The list of request Content-Type headers
* @param returnType The return type into which to deserialize the response
* @param isBodyNullable True if the body is nullable
* @return The future which be fired with the response
*/
public CompletableFuture> invokeAPIAsync(
String method,
Invocation.Builder invocationBuilder,
Map headerParams,
String[] contentTypes,
Object body,
Map formParams,
Boolean isBodyNullable,
GenericType returnType) {
String contentEncoding = headerParams.get(HttpHeaders.CONTENT_ENCODING);
Entity> entity =
serialize(
body,
formParams,
selectHeaderContentType(contentTypes),
contentEncoding,
isBodyNullable);
CompletableFuture> result = new CompletableFuture<>();
InvocationCallback callback =
new InvocationCallback() {
@Override
public void completed(Response response) {
int statusCode = response.getStatusInfo().getStatusCode();
Map> responseHeaders = buildResponseHeaders(response);
if (response.getStatusInfo() == Status.NO_CONTENT) {
result.complete(new ApiResponse(statusCode, responseHeaders));
} else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) {
if (returnType == null) {
result.complete(new ApiResponse(statusCode, responseHeaders));
} else {
result.complete(
new ApiResponse(
statusCode, responseHeaders, deserialize(response, returnType)));
}
} else {
String message = "error";
String respBody = null;
if (response.hasEntity()) {
try {
respBody = String.valueOf(response.readEntity(String.class));
message = respBody;
} catch (RuntimeException e) {
}
}
result.completeExceptionally(
new ApiException(
response.getStatus(), message, buildResponseHeaders(response), respBody));
}
}
@Override
public void failed(Throwable throwable) {
result.completeExceptionally(throwable);
}
};
// XXX Handle 401 for OAuth
sendRequestAsync(method, invocationBuilder, entity, callback);
return result;
}
private Future sendRequestAsync(
String method,
Invocation.Builder invocationBuilder,
Entity> entity,
InvocationCallback callback) {
Future response;
AsyncInvoker invoker = invocationBuilder.async();
if ("POST".equals(method)) {
response = invoker.post(entity, callback);
} else if ("PUT".equals(method)) {
response = invoker.put(entity, callback);
} else if ("DELETE".equals(method)) {
response = invoker.method("DELETE", entity, callback);
} else if ("PATCH".equals(method)) {
response = invoker.method("PATCH", entity, callback);
} else {
response = invoker.method(method, callback);
}
return response;
}
/**
* Build the Client used to make HTTP requests.
*
* @return Client
*/
protected Client buildHttpClient() {
// use the default client config if not yet initialized
if (clientConfig == null) {
clientConfig = getDefaultClientConfig();
}
if (compress) {
clientConfig.register(EncodingFilter.class);
}
clientConfig.register(GZipEncoder.class);
clientConfig.register(DeflateEncoder.class);
clientConfig.register(ZstdEncoder.class);
ClientBuilder clientBuilder = ClientBuilder.newBuilder();
customizeClientBuilder(clientBuilder);
clientBuilder = clientBuilder.withConfig(clientConfig);
return clientBuilder.build();
}
/**
* Get the default client config.
*
* @return Client config
*/
public ClientConfig getDefaultClientConfig() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(MultiPartFeature.class);
clientConfig.register(json);
clientConfig.register(JacksonFeature.class);
clientConfig.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
// turn off compliance validation to be able to send payloads with DELETE calls
clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
if (debugging) {
clientConfig.register(
new LoggingFeature(
java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME),
java.util.logging.Level.INFO,
LoggingFeature.Verbosity.PAYLOAD_ANY,
1024 * 50 /* Log payloads up to 50K */));
clientConfig.property(
LoggingFeature.LOGGING_FEATURE_VERBOSITY, LoggingFeature.Verbosity.PAYLOAD_ANY);
// Set logger to ALL
java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)
.setLevel(java.util.logging.Level.ALL);
} else {
// suppress warnings for payloads with DELETE calls:
java.util.logging.Logger.getLogger("org.glassfish.jersey.client")
.setLevel(java.util.logging.Level.SEVERE);
}
return clientConfig;
}
/**
* Customize the client builder.
*
* This method can be overridden to customize the API client. For example, this can be used to:
* 1. Set the hostname verifier to be used by the client to verify the endpoint's hostname against
* its identification information. 2. Set the client-side key store. 3. Set the SSL context that
* will be used when creating secured transport connections to server endpoints from web targets
* created by the client instance that is using this SSL context. 4. Set the client-side trust
* store.
*
*
To completely disable certificate validation (at your own risk), you can override this
* method and invoke disableCertificateValidation(clientBuilder).
*
* @param clientBuilder: HTTP client builder
*/
protected void customizeClientBuilder(ClientBuilder clientBuilder) {
// No-op extension point
}
/**
* Disable X.509 certificate validation in TLS connections.
*
*
Please note that trusting all certificates is extremely risky. This may be useful in a
* development environment with self-signed certificates.
*
* @param clientBuilder: HTTP client builder
* @throws KeyManagementException When the SSL context can't be initialized
* @throws NoSuchAlgorithmException If the environment doesn't support the required algorithm
*/
protected void disableCertificateValidation(ClientBuilder clientBuilder)
throws KeyManagementException, NoSuchAlgorithmException {
TrustManager[] trustAllCerts =
new X509TrustManager[] {
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
clientBuilder.sslContext(sslContext);
}
protected Map> buildResponseHeaders(Response response) {
Map> responseHeaders = new HashMap>();
for (Entry> entry : response.getHeaders().entrySet()) {
List