summaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/onap/aai/restclient/client/RestClient.java1503
-rw-r--r--src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java483
-rw-r--r--src/main/resources/logging/RESTClientMsgs.properties4
3 files changed, 980 insertions, 1010 deletions
diff --git a/src/main/java/org/onap/aai/restclient/client/RestClient.java b/src/main/java/org/onap/aai/restclient/client/RestClient.java
index f4f184c..89d5c62 100644
--- a/src/main/java/org/onap/aai/restclient/client/RestClient.java
+++ b/src/main/java/org/onap/aai/restclient/client/RestClient.java
@@ -23,869 +23,854 @@ package org.onap.aai.restclient.client;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-
+import java.util.stream.Collectors;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
-
-import org.onap.aai.restclient.enums.RestAuthenticationMode;
-import org.onap.aai.restclient.logging.RestClientMsgs;
-import org.onap.aai.restclient.rest.RestClientBuilder;
import org.onap.aai.cl.api.LogFields;
import org.onap.aai.cl.api.LogLine;
import org.onap.aai.cl.api.Logger;
import org.onap.aai.cl.eelf.LoggerFactory;
import org.onap.aai.cl.mdc.MdcContext;
import org.onap.aai.cl.mdc.MdcOverride;
+import org.onap.aai.restclient.enums.RestAuthenticationMode;
+import org.onap.aai.restclient.logging.RestClientMsgs;
+import org.onap.aai.restclient.rest.RestClientBuilder;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.WebResource.Builder;
-import com.sun.jersey.core.util.MultivaluedMapImpl;
/**
- * This class provides a general client implementation that micro services can use for communicating
- * with the endpoints via their exposed REST interfaces.
+ * This class provides a general client implementation that micro services can use for communicating with the endpoints
+ * via their exposed REST interfaces.
*
*/
public class RestClient {
- /**
- * This is a generic builder that is used for constructing the REST client that we will use to
- * communicate with the REST endpoint.
- */
- private RestClientBuilder clientBuilder;
-
- private final ConcurrentMap<String,InitializedClient> CLIENT_CACHE = new ConcurrentHashMap<>();
- private static final String REST_CLIENT_INSTANCE = "REST_CLIENT_INSTANCE";
-
- /** Standard logger for producing log statements. */
- private Logger logger = LoggerFactory.getInstance().getLogger("AAIRESTClient");
-
- /** Standard logger for producing metric statements. */
- private Logger metricsLogger = LoggerFactory.getInstance().getMetricsLogger("AAIRESTClient");
-
- private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
-
- /** Reusable function call for GET REST operations. */
- private final RestOperation getOp = new GetRestOperation();
-
- /** Reusable function call for PUT REST operations. */
- private final RestOperation putOp = new PutRestOperation();
-
- /** Reusable function call for POST REST operations. */
- private final RestOperation postOp = new PostRestOperation();
-
- /** Reusable function call for DELETE REST operations. */
- private final RestOperation deleteOp = new DeleteRestOperation();
-
- /** Reusable function call for HEAD REST operations. */
- private final RestOperation headOp = new HeadRestOperation();
-
- /** Reusable function call for PATCH REST operations. */
- private final RestOperation patchOp = new PatchRestOperation();
-
-
- /**
- * Creates a new instance of the {@link RestClient}.
- */
- public RestClient() {
-
- clientBuilder = new RestClientBuilder();
-
- }
-
-
- /**
- * Creates a new instance of the {@link RestClient} using the supplied {@link RestClientBuilder}.
- *
- * @param rcBuilder - The REST client builder that this instance of the {@link RestClient} should
- * use.
- */
- public RestClient(RestClientBuilder rcBuilder) {
- clientBuilder = rcBuilder;
- }
-
- public RestClient authenticationMode(RestAuthenticationMode mode) {
- logger.debug("Set rest authentication mode= " + mode);
- clientBuilder.setAuthenticationMode(mode);
- return this;
- }
-
- public RestClient basicAuthUsername(String username) {
- logger.debug("Set SSL BasicAuth username = " + username);
- clientBuilder.setBasicAuthUsername(username);
- return this;
- }
-
- public RestClient basicAuthPassword(String password) {
- /*
- * purposely not logging out the password, I guess we could obfuscate it if we really want to
- * see it in the logs
+ /**
+ * This is a generic builder that is used for constructing the REST client that we will use to communicate with the
+ * REST endpoint.
*/
- clientBuilder.setBasicAuthPassword(password);
- return this;
- }
-
-
- /**
- * Sets the flag to indicate whether or not validation should be performed against the host name
- * of the server we are trying to communicate with.
- *
- * @parameter validate - Set to true to enable validation, false to disable
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient validateServerHostname(boolean validate) {
- logger.debug("Set validate server hostname = " + validate);
- clientBuilder.setValidateServerHostname(validate);
- return this;
- }
-
-
- /**
- * Sets the flag to indicate whether or not validation should be performed against the certificate
- * chain.
- *
- * @parameter validate - Set to true to enable validation, false to disable.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient validateServerCertChain(boolean validate) {
- logger.debug("Set validate server certificate chain = " + validate);
- clientBuilder.setValidateServerCertChain(validate);
- return this;
- }
-
-
- /**
- * Assigns the client certificate file to use.
- *
- * @param filename - The name of the certificate file.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient clientCertFile(String filename) {
- logger.debug("Set client certificate filename = " + filename);
- clientBuilder.setClientCertFileName(filename);
- return this;
- }
-
-
- /**
- * Assigns the client certificate password to use.
- *
- * @param password - The certificate password.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient clientCertPassword(String password) {
- clientBuilder.setClientCertPassword(password);
- return this;
- }
-
-
- /**
- * Assigns the name of the trust store file to use.
- *
- * @param filename - the name of the trust store file.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient trustStore(String filename) {
- logger.debug("Set trust store filename = " + filename);
- clientBuilder.setTruststoreFilename(filename);
- return this;
- }
-
-
- /**
- * Assigns the connection timeout (in ms) to use when connecting to the target server.
- *
- * @param timeout - The length of time to wait in ms before timing out.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient connectTimeoutMs(int timeout) {
- logger.debug("Set connection timeout = " + timeout + " ms");
- clientBuilder.setConnectTimeoutInMs(timeout);
- return this;
- }
-
-
- /**
- * Assigns the read timeout (in ms) to use when communicating with the target server.
- *
- * @param timeout The read timeout in milliseconds.
- *
- * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
- */
- public RestClient readTimeoutMs(int timeout) {
- logger.debug("Set read timeout = " + timeout + " ms");
- clientBuilder.setReadTimeoutInMs(timeout);
- return this;
- }
-
- /**
- * Configures the client for a specific SSL protocol
- *
- * @param sslProtocol - protocol string constant such as TLS, TLSv1, TLSv1.1, TLSv1.2
- *
- * @return The AAIRESTClient instance.
- */
- public RestClient sslProtocol(String sslProtocol) {
- logger.debug("Set sslProtocol = " + sslProtocol);
- clientBuilder.setSslProtocol(sslProtocol);
- return this;
- }
-
- private boolean shouldRetry(OperationResult operationResult) {
-
- if (operationResult == null) {
- return true;
- }
-
- int resultCode = operationResult.getResultCode();
-
- if (resultCode == 200) {
- return false;
- }
-
- if (resultCode == 404) {
- return false;
- }
-
- return true;
-
- }
-
- /**
- * This method operates on a REST endpoint by submitting an HTTP operation request against the
- * supplied URL.
- * This variant of the method will perform a requested number of retries in the event that the
- * first request is unsuccessful.
- *
- * @param operation - the REST operation type to send to the url
- * @param url - The REST endpoint to submit the REST request to.
- * @param payload - They payload to provide in the REST request, if applicable
- * @param headers - The headers that should be passed in the request
- * @param contentType - The content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return The result of the REST request.
- */
- protected OperationResult processRequest(RestOperation operation, String url, String payload,
- Map<String, List<String>> headers, MediaType contentType, MediaType responseType,
- int numRetries) {
-
-
- OperationResult result = null;
-
- long startTimeInMs = System.currentTimeMillis();
- for (int retryCount = 0; retryCount < numRetries; retryCount++) {
-
- logger.info(RestClientMsgs.HTTP_REQUEST_WITH_RETRIES, operation.getRequestType().toString(),
- url, Integer.toString(retryCount + 1));
-
- // Submit our query to the AAI.
- result = processRequest(operation, url, payload, headers, contentType, responseType);
-
- // If the submission was successful then we're done.
-
- if (!shouldRetry(result)) {
-
- logger.info(RestClientMsgs.HTTP_REQUEST_TIME_WITH_RETRIES, operation.getRequestType().toString(),url,
- Long.toString(System.currentTimeMillis() - startTimeInMs),
- Integer.toString(retryCount));
-
- result.setNumRetries(retryCount);
-
- return result;
- }
+ private RestClientBuilder clientBuilder;
+
+ private final ConcurrentMap<String, InitializedClient> CLIENT_CACHE = new ConcurrentHashMap<>();
+ private static final String REST_CLIENT_INSTANCE = "REST_CLIENT_INSTANCE";
+
+ /** Standard logger for producing log statements. */
+ private Logger logger = LoggerFactory.getInstance().getLogger("AAIRESTClient");
+
+ /** Standard logger for producing metric statements. */
+ private Logger metricsLogger = LoggerFactory.getInstance().getMetricsLogger("AAIRESTClient");
- // Our submission was unsuccessful...
- try {
- // Sleep between re-tries to be nice to the target system.
- Thread.sleep(50);
-
- } catch (InterruptedException e) {
- logger.error(RestClientMsgs.HTTP_REQUEST_INTERRUPTED, url, e.getLocalizedMessage());
- Thread.currentThread().interrupt();
- break;
- }
+ private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
+
+ /** Reusable function call for GET REST operations. */
+ private final RestOperation getOp = new GetRestOperation();
+
+ /** Reusable function call for PUT REST operations. */
+ private final RestOperation putOp = new PutRestOperation();
+
+ /** Reusable function call for POST REST operations. */
+ private final RestOperation postOp = new PostRestOperation();
+
+ /** Reusable function call for DELETE REST operations. */
+ private final RestOperation deleteOp = new DeleteRestOperation();
+
+ /** Reusable function call for HEAD REST operations. */
+ private final RestOperation headOp = new HeadRestOperation();
+
+ /** Reusable function call for PATCH REST operations. */
+ private final RestOperation patchOp = new PatchRestOperation();
+
+ /**
+ * Creates a new instance of the {@link RestClient}.
+ */
+ public RestClient() {
+ clientBuilder = new RestClientBuilder();
}
- // If we've gotten this far, then we failed all of our retries.
- if (result == null) {
- result = new OperationResult();
- }
-
- result.setNumRetries(numRetries);
- result.setResultCode(504);
- result.setFailureCause("Failed to get a successful result after multiple retries to target server.");
-
-
- return result;
- }
-
- /**
- * This method operates on a REST endpoint by submitting an HTTP operation request against the
- * supplied URL.
- *
- * @param operation - the REST operation type to send to the url
- * @param url - The REST endpoint to submit the REST request to.
- * @param payload - They payload to provide in the REST request, if applicable
- * @param headers - The headers that should be passed in the request
- * @param contentType - The content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return The result of the REST request.
- */
- protected OperationResult processRequest(RestOperation operation, String url, String payload,
- Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
-
- ClientResponse clientResponse = null;
- OperationResult operationResult = new OperationResult();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- String requestType = operation.getRequestType().name();
-
- // Grab the current time so that we can log how long the
- // query took once we are done.
- long startTimeInMs = System.currentTimeMillis();
- MdcOverride override = new MdcOverride();
- override.addAttribute(MdcContext.MDC_START_TIME, formatter.format(startTimeInMs));
-
- logger.info(RestClientMsgs.HTTP_REQUEST, requestType, url);
-
- try {
-
- // Get a REST client instance for our request.
- Client client = getClient();
-
- // Debug log the request
- debugRequest(url, payload, headers, responseType);
-
- // Get a client request builder, and submit our GET request.
- Builder builder = getClientBuilder(client, url, payload, headers, contentType, responseType);
- clientResponse = operation.processOperation(builder);
-
- populateOperationResult(clientResponse, operationResult);
-
- // Debug log the response
- if (clientResponse != null) {
- debugResponse(operationResult, clientResponse.getHeaders());
- }
-
- } catch (Exception ex) {
-
- logger.error(RestClientMsgs.HTTP_REQUEST_ERROR, requestType, url, ex.getLocalizedMessage());
- operationResult.setResultCode(500);
- operationResult.setFailureCause(
- "Error during GET operation to AAI with message = " + ex.getLocalizedMessage());
-
- } finally {
-
- if (logger.isDebugEnabled()) {
- logger.debug(baos.toString());
- }
-
- // Not every valid response code is actually represented by the Response.Status
- // object, so we need to guard against missing codes, otherwise we throw null
- // pointer exceptions when we try to generate our metrics logs...
- Response.Status responseStatus =
- Response.Status.fromStatusCode(operationResult.getResultCode());
- String responseStatusCodeString = "";
- if (responseStatus != null) {
- responseStatusCodeString = responseStatus.toString();
- }
-
- metricsLogger.info(RestClientMsgs.HTTP_REQUEST_TIME,
- new LogFields().setField(LogLine.DefinedFields.STATUS_CODE, responseStatusCodeString)
- .setField(LogLine.DefinedFields.RESPONSE_CODE, operationResult.getResultCode())
- .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, operationResult.getResult()),
- override, requestType, Long.toString(System.currentTimeMillis() - startTimeInMs), url);
- logger.info(RestClientMsgs.HTTP_REQUEST_TIME, requestType,
- Long.toString(System.currentTimeMillis() - startTimeInMs), url);
- logger.info(RestClientMsgs.HTTP_RESPONSE, url,
- operationResult.getResultCode() + " " + responseStatusCodeString);
- }
-
- return operationResult;
- }
-
- /**
- * This method submits an HTTP PUT request against the supplied URL.
- *
- * @param url - The REST endpoint to submit the PUT request to.
- * @param payload - the payload to send to the supplied URL
- * @param headers - The headers that should be passed in the request
- * @param contentType - The content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return The result of the PUT request.
- */
- public OperationResult put(String url, String payload, Map<String, List<String>> headers,
- MediaType contentType, MediaType responseType) {
- return processRequest(putOp, url, payload, headers, contentType, responseType);
- }
-
- /**
- * This method submits an HTTP POST request against the supplied URL.
- *
- * @param url - The REST endpoint to submit the POST request to.
- * @param payload - the payload to send to the supplied URL
- * @param headers - The headers that should be passed in the request
- * @param contentType - The content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return The result of the POST request.
- */
- public OperationResult post(String url, String payload, Map<String, List<String>> headers,
- MediaType contentType, MediaType responseType) {
- return processRequest(postOp, url, payload, headers, contentType, responseType);
- }
-
- /**
- * This method submits an HTTP POST request against the supplied URL, and emulates a PATCH
- * operation by setting a special header value
- *
- * @param url - The REST endpoint to submit the POST request to.
- * @param payload - the payload to send to the supplied URL
- * @param headers - The headers that should be passed in the request
- * @param contentType - The content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return The result of the POST request.
- */
- public OperationResult patch(String url, String payload, Map<String, List<String>> headers,
- MediaType contentType, MediaType responseType) {
- return processRequest(patchOp, url, payload, headers, contentType, responseType);
- }
-
-
- /**
- * This method submits an HTTP HEAD request against the supplied URL
- *
- * @param url - The REST endpoint to submit the POST request to.
- * @param headers - The headers that should be passed in the request
- * @param responseType - The expected format of the response.
- *
- * @return The result of the POST request.
- */
- public OperationResult head(String url, Map<String, List<String>> headers,
- MediaType responseType) {
- return processRequest(headOp, url, null, headers, null, responseType);
- }
-
- /**
- * This method submits an HTTP GET request against the supplied URL.
- *
- * @param url - The REST endpoint to submit the GET request to.
- * @param headers - The headers that should be passed in the request
- * @param responseType - The expected format of the response.
- *
- * @return The result of the GET request.
- */
- public OperationResult get(String url, Map<String, List<String>> headers,
- MediaType responseType) {
- return processRequest(getOp, url, null, headers, null, responseType);
- }
-
- /**
- * This method submits an HTTP GET request against the supplied URL.
- * This variant of the method will perform a requested number of retries in the event that the
- * first request is unsuccessful.
- *
- * @param url - The REST endpoint to submit the GET request to.
- * @param headers - The headers that should be passed in the request
- * @param responseType - The expected format of the response.
- * @param numRetries - The number of times to try resubmitting the request in the event of a
- * failure.
- *
- * @return The result of the GET request.
- */
- public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType,
- int numRetries) {
- return processRequest(getOp, url, null, headers, null, responseType, numRetries);
- }
-
- /**
- * This method submits an HTTP DELETE request against the supplied URL.
- *
- * @param url - The REST endpoint to submit the DELETE request to.
- * @param headers - The headers that should be passed in the request
- * @param responseType - The expected format of the response.
- *
- * @return The result of the DELETE request.
- */
- public OperationResult delete(String url, Map<String, List<String>> headers,
- MediaType responseType) {
- return processRequest(deleteOp, url, null, headers, null, responseType);
- }
-
- /**
- * This method does a health check ("ping") against the supplied URL.
- *
- * @param url - The REST endpoint to attempt a health check.
- * @param srcAppName - The name of the application using this client.
- * @param destAppName - The name of the destination app.
- *
- * @return A boolean value. True if connection attempt was successful, false otherwise.
- *
- */
- public boolean healthCheck(String url, String srcAppName, String destAppName) {
- return healthCheck(url, srcAppName, destAppName, MediaType.TEXT_PLAIN_TYPE);
-
- }
-
- /**
- * This method does a health check ("ping") against the supplied URL.
- *
- * @param url - The REST endpoint to attempt a health check.
- * @param srcAppName - The name of the application using this client.
- * @param destAppName - The name of the destination app.
- * @param responseType - The response type.
- *
- * @return A boolean value. True if connection attempt was successful, false otherwise.
- *
- */
- public boolean healthCheck(String url, String srcAppName, String destAppName,
- MediaType responseType) {
- MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
- headers.put(Headers.FROM_APP_ID, Arrays.asList(new String[] {srcAppName}));
- headers.put(Headers.TRANSACTION_ID, Arrays.asList(new String[] {UUID.randomUUID().toString()}));
-
- try {
- logger.info(RestClientMsgs.HEALTH_CHECK_ATTEMPT, destAppName, url);
- OperationResult result = get(url, headers, responseType);
-
- if (result != null && result.getFailureCause() == null) {
- logger.info(RestClientMsgs.HEALTH_CHECK_SUCCESS, destAppName, url);
- return true;
- } else {
- logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, result != null ? result.getFailureCause()
- : null);
- return false;
- }
- } catch (Exception e) {
- logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, e.getMessage());
- return false;
+ /**
+ * Creates a new instance of the {@link RestClient} using the supplied {@link RestClientBuilder}.
+ *
+ * @param rcBuilder - The REST client builder that this instance of the {@link RestClient} should use.
+ */
+ public RestClient(RestClientBuilder rcBuilder) {
+ clientBuilder = rcBuilder;
}
- }
- /**
- * This method constructs a client request builder that can be used for submitting REST requests
- * to the supplied URL endpoint.
- *
- * @param client - The REST client we will be using to talk to the server.
- * @param url - The URL endpoint that our request will be submitted to.
- * @param headers - The headers that should be passed in the request
- * @param contentType - the content type of the payload
- * @param responseType - The expected format of the response.
- *
- * @return A client request builder.
- */
- private Builder getClientBuilder(Client client, String url, String payload,
- Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
+ public RestClient authenticationMode(RestAuthenticationMode mode) {
+ logger.debug("Set rest authentication mode= " + mode);
+ clientBuilder.setAuthenticationMode(mode);
+ return this;
+ }
- WebResource resource = client.resource(url);
- Builder builder = resource.accept(responseType);
+ public RestClient basicAuthUsername(String username) {
+ logger.debug("Set SSL BasicAuth username = " + username);
+ clientBuilder.setBasicAuthUsername(username);
+ return this;
+ }
- if (contentType != null) {
- builder.type(contentType);
+ public RestClient basicAuthPassword(String password) {
+ /*
+ * purposely not logging out the password, I guess we could obfuscate it if we really want to see it in the logs
+ */
+ clientBuilder.setBasicAuthPassword(password);
+ return this;
}
- if (payload != null) {
- builder.entity(payload);
+ /**
+ * Sets the flag to indicate whether or not validation should be performed against the host name of the server we
+ * are trying to communicate with.
+ *
+ * @parameter validate - Set to true to enable validation, false to disable
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient validateServerHostname(boolean validate) {
+ logger.debug("Set validate server hostname = " + validate);
+ clientBuilder.setValidateServerHostname(validate);
+ return this;
}
- if (headers != null) {
- for (Entry<String, List<String>> header : headers.entrySet()) {
- builder.header(header.getKey(), String.join(";",header.getValue()));
- }
-
- //Added additional check to prevent adding duplicate authorization header if client is already sending the authorization header
- // AAI-1097 - For AAI calls when Rest authentication mode is selected as SSL_BASIC getting 403 error
- if (clientBuilder.getAuthenticationMode() == RestAuthenticationMode.SSL_BASIC && headers.get(Headers.AUTHORIZATION) == null) {
- builder = builder.header(Headers.AUTHORIZATION,
- clientBuilder.getBasicAuthenticationCredentials());
- }
-
+ /**
+ * Sets the flag to indicate whether or not validation should be performed against the certificate chain.
+ *
+ * @parameter validate - Set to true to enable validation, false to disable.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient validateServerCertChain(boolean validate) {
+ logger.debug("Set validate server certificate chain = " + validate);
+ clientBuilder.setValidateServerCertChain(validate);
+ return this;
}
- return builder;
- }
+ /**
+ * Assigns the client certificate file to use.
+ *
+ * @param filename - The name of the certificate file.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient clientCertFile(String filename) {
+ logger.debug("Set client certificate filename = " + filename);
+ clientBuilder.setClientCertFileName(filename);
+ return this;
+ }
- private void debugRequest(String url, String payload, Map<String, List<String>> headers,
- MediaType responseType) {
- if (!logger.isDebugEnabled()) {
- return;
+ /**
+ * Assigns the client certificate password to use.
+ *
+ * @param password - The certificate password.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient clientCertPassword(String password) {
+ clientBuilder.setClientCertPassword(password);
+ return this;
}
- StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
- debugRequest.append("URL: ").append(url).append("\n");
- debugRequest.append("Payload: ").append(payload).append("\n");
- debugRequest.append("Response Type: ").append(responseType).append("\n");
+ /**
+ * Assigns the name of the trust store file to use.
+ *
+ * @param filename - the name of the trust store file.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient trustStore(String filename) {
+ logger.debug("Set trust store filename = " + filename);
+ clientBuilder.setTruststoreFilename(filename);
+ return this;
+ }
- if (headers == null) {
- logger.debug(debugRequest.toString());
- return;
+ /**
+ * Assigns the connection timeout (in ms) to use when connecting to the target server.
+ *
+ * @param timeout - The length of time to wait in ms before timing out.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient connectTimeoutMs(int timeout) {
+ logger.debug("Set connection timeout = " + timeout + " ms");
+ clientBuilder.setConnectTimeoutInMs(timeout);
+ return this;
}
- debugRequest.append("Headers: ");
- for (Entry<String, List<String>> header : headers.entrySet()) {
- debugRequest.append("\n\t").append(header.getKey()).append(":");
- for (String headerEntry : header.getValue()) {
- debugRequest.append("\"").append(headerEntry).append("\" ");
- }
+ /**
+ * Assigns the read timeout (in ms) to use when communicating with the target server.
+ *
+ * @param timeout The read timeout in milliseconds.
+ *
+ * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
+ */
+ public RestClient readTimeoutMs(int timeout) {
+ logger.debug("Set read timeout = " + timeout + " ms");
+ clientBuilder.setReadTimeoutInMs(timeout);
+ return this;
}
- logger.debug(debugRequest.toString());
+ /**
+ * Configures the client for a specific SSL protocol
+ *
+ * @param sslProtocol - protocol string constant such as TLS, TLSv1, TLSv1.1, TLSv1.2
+ *
+ * @return The AAIRESTClient instance.
+ */
+ public RestClient sslProtocol(String sslProtocol) {
+ logger.debug("Set sslProtocol = " + sslProtocol);
+ clientBuilder.setSslProtocol(sslProtocol);
+ return this;
+ }
+
+ private boolean shouldRetry(OperationResult operationResult) {
+
+ if (operationResult == null) {
+ return true;
+ }
+
+ int resultCode = operationResult.getResultCode();
+
+ if (resultCode == 200) {
+ return false;
+ }
- }
+ if (resultCode == 404) {
+ return false;
+ }
- private void debugResponse(OperationResult operationResult,
- MultivaluedMap<String, String> headers) {
+ return true;
- if (!logger.isDebugEnabled()) {
- return;
}
- StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
- debugResponse.append("Result: ").append(operationResult.getResultCode()).append("\n");
- debugResponse.append("Failure Cause: ").append(operationResult.getFailureCause()).append("\n");
- debugResponse.append("Payload: ").append(operationResult.getResult()).append("\n");
+ /**
+ * This method operates on a REST endpoint by submitting an HTTP operation request against the supplied URL. This
+ * variant of the method will perform a requested number of retries in the event that the first request is
+ * unsuccessful.
+ *
+ * @param operation - the REST operation type to send to the url
+ * @param url - The REST endpoint to submit the REST request to.
+ * @param payload - They payload to provide in the REST request, if applicable
+ * @param headers - The headers that should be passed in the request
+ * @param contentType - The content type of the payload
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the REST request.
+ */
+ protected OperationResult processRequest(RestOperation operation, String url, String payload,
+ Map<String, List<String>> headers, MediaType contentType, MediaType responseType, int numRetries) {
+
+
+ OperationResult result = null;
- if (headers == null) {
- logger.debug(debugResponse.toString());
- return;
+ long startTimeInMs = System.currentTimeMillis();
+ for (int retryCount = 0; retryCount < numRetries; retryCount++) {
+
+ logger.info(RestClientMsgs.HTTP_REQUEST_WITH_RETRIES, operation.getRequestType().toString(), url,
+ Integer.toString(retryCount + 1));
+
+ // Submit our query to the AAI.
+ result = processRequest(operation, url, payload, headers, contentType, responseType);
+
+ // If the submission was successful then we're done.
+
+ if (!shouldRetry(result)) {
+
+ logger.info(RestClientMsgs.HTTP_REQUEST_TIME_WITH_RETRIES, operation.getRequestType().toString(), url,
+ Long.toString(System.currentTimeMillis() - startTimeInMs), Integer.toString(retryCount));
+
+ result.setNumRetries(retryCount);
+
+ return result;
+ }
+
+ // Our submission was unsuccessful...
+ try {
+ // Sleep between re-tries to be nice to the target system.
+ Thread.sleep(50);
+
+ } catch (InterruptedException e) {
+ logger.error(RestClientMsgs.HTTP_REQUEST_INTERRUPTED, url, e.getLocalizedMessage());
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+
+ // If we've gotten this far, then we failed all of our retries.
+ if (result == null) {
+ result = new OperationResult();
+ }
+
+ result.setNumRetries(numRetries);
+ result.setResultCode(504);
+ result.setFailureCause("Failed to get a successful result after multiple retries to target server.");
+
+
+ return result;
}
- debugResponse.append("Headers: ");
- for (Entry<String, List<String>> header : headers.entrySet()) {
- debugResponse.append("\n\t").append(header.getKey()).append(":");
- for (String headerEntry : header.getValue()) {
- debugResponse.append("\"").append(headerEntry).append("\" ");
- }
+ /**
+ * This method operates on a REST endpoint by submitting an HTTP operation request against the supplied URL.
+ *
+ * @param operation - the REST operation type to send to the url
+ * @param url - The REST endpoint to submit the REST request to.
+ * @param payload - They payload to provide in the REST request, if applicable
+ * @param headers - The headers that should be passed in the request
+ * @param contentType - The content type of the payload
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the REST request.
+ */
+ protected OperationResult processRequest(RestOperation operation, String url, String payload,
+ Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
+
+ Response clientResponse = null;
+ OperationResult operationResult = new OperationResult();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ String requestType = operation.getRequestType().name();
+
+ // Grab the current time so that we can log how long the
+ // query took once we are done.
+ long startTimeInMs = System.currentTimeMillis();
+ MdcOverride override = new MdcOverride();
+ override.addAttribute(MdcContext.MDC_START_TIME, formatter.format(startTimeInMs));
+
+ logger.info(RestClientMsgs.HTTP_REQUEST, requestType, url);
+
+ try {
+
+ // Get a REST client instance for our request.
+ Client client = getClient();
+
+ // Debug log the request
+ debugRequest(url, payload, headers, responseType);
+
+ // Get a client request builder, and submit our GET request.
+ Builder builder = getClientBuilder(client, url, headers, responseType);
+ clientResponse = operation.processOperation(builder, payload, contentType);
+
+ populateOperationResult(clientResponse, operationResult);
+
+ // Debug log the response
+ if (clientResponse != null) {
+ debugResponse(operationResult, clientResponse.getHeaders());
+ }
+
+ } catch (Exception ex) {
+
+ logger.error(RestClientMsgs.HTTP_REQUEST_ERROR, requestType, url, ex.getLocalizedMessage());
+ operationResult.setResultCode(500);
+ operationResult
+ .setFailureCause("Error during GET operation to AAI with message = " + ex.getLocalizedMessage());
+
+ } finally {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(baos.toString());
+ }
+
+ // Not every valid response code is actually represented by the Response.Status
+ // object, so we need to guard against missing codes, otherwise we throw null
+ // pointer exceptions when we try to generate our metrics logs...
+ Response.Status responseStatus = Response.Status.fromStatusCode(operationResult.getResultCode());
+ String responseStatusCodeString = "";
+ if (responseStatus != null) {
+ responseStatusCodeString = responseStatus.toString();
+ }
+
+ metricsLogger.info(RestClientMsgs.HTTP_REQUEST_TIME,
+ new LogFields().setField(LogLine.DefinedFields.STATUS_CODE, responseStatusCodeString)
+ .setField(LogLine.DefinedFields.RESPONSE_CODE, operationResult.getResultCode())
+ .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, operationResult.getResult()),
+ override, requestType, Long.toString(System.currentTimeMillis() - startTimeInMs), url);
+ logger.info(RestClientMsgs.HTTP_REQUEST_TIME, requestType,
+ Long.toString(System.currentTimeMillis() - startTimeInMs), url);
+ logger.info(RestClientMsgs.HTTP_RESPONSE, url,
+ operationResult.getResultCode() + " " + responseStatusCodeString);
+ }
+
+ return operationResult;
}
- logger.debug(debugResponse.toString());
- }
+ /**
+ * This method submits an HTTP PUT request against the supplied URL.
+ *
+ * @param url - The REST endpoint to submit the PUT request to.
+ * @param payload - the payload to send to the supplied URL
+ * @param headers - The headers that should be passed in the request
+ * @param contentType - The content type of the payload
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the PUT request.
+ */
+ public OperationResult put(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
+ MediaType responseType) {
+ return processRequest(putOp, url, payload, headers, contentType, responseType);
+ }
- /**
- * This method creates an instance of the low level REST client to use for communicating with the
- * AAI, if one has not already been created, otherwise it returns the already created instance.
- *
- * @return A {@link Client} instance.
- */
- protected Client getClient() throws Exception {
+ /**
+ * This method submits an HTTP POST request against the supplied URL.
+ *
+ * @param url - The REST endpoint to submit the POST request to.
+ * @param payload - the payload to send to the supplied URL
+ * @param headers - The headers that should be passed in the request
+ * @param contentType - The content type of the payload
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the POST request.
+ */
+ public OperationResult post(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
+ MediaType responseType) {
+ return processRequest(postOp, url, payload, headers, contentType, responseType);
+ }
- /*
- * Attempting a new way of doing non-blocking thread-safe lazy-initialization by using Java 1.8
- * computeIfAbsent functionality. A null value will not be stored, but once a valid mapping has
- * been established, then the same value will be returned.
+ /**
+ * This method submits an HTTP POST request against the supplied URL, and emulates a PATCH operation by setting a
+ * special header value
+ *
+ * @param url - The REST endpoint to submit the POST request to.
+ * @param payload - the payload to send to the supplied URL
+ * @param headers - The headers that should be passed in the request
+ * @param contentType - The content type of the payload
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the PATCH request.
+ */
+ public OperationResult patch(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
+ MediaType responseType) {
+ return processRequest(patchOp, url, payload, headers, contentType, responseType);
+ }
+
+ /**
+ * This method submits an HTTP HEAD request against the supplied URL
+ *
+ * @param url - The REST endpoint to submit the POST request to.
+ * @param headers - The headers that should be passed in the request
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the HEAD request.
+ */
+ public OperationResult head(String url, Map<String, List<String>> headers, MediaType responseType) {
+ return processRequest(headOp, url, null, headers, null, responseType);
+ }
+
+ /**
+ * This method submits an HTTP GET request against the supplied URL.
+ *
+ * @param url - The REST endpoint to submit the GET request to.
+ * @param headers - The headers that should be passed in the request
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the GET request.
+ */
+ public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType) {
+ return processRequest(getOp, url, null, headers, null, responseType);
+ }
+
+ /**
+ * This method submits an HTTP GET request against the supplied URL. This variant of the method will perform a
+ * requested number of retries in the event that the first request is unsuccessful.
+ *
+ * @param url - The REST endpoint to submit the GET request to.
+ * @param headers - The headers that should be passed in the request
+ * @param responseType - The expected format of the response.
+ * @param numRetries - The number of times to try resubmitting the request in the event of a failure.
*
- * One awkwardness of the computeIfAbsent is the lack of support for thrown exceptions, which
- * required a bit of hoop jumping to preserve the original exception for the purpose of
- * maintaining the pre-existing this API signature.
+ * @return The result of the GET request.
*/
-
- final InitializedClient clientInstance =
- CLIENT_CACHE.computeIfAbsent(REST_CLIENT_INSTANCE, k -> loggedClientInitialization());
-
- if (clientInstance.getCaughtException() != null) {
- throw new InstantiationException(clientInstance.getCaughtException().getMessage());
+ public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType, int numRetries) {
+ return processRequest(getOp, url, null, headers, null, responseType, numRetries);
}
- return clientInstance.getClient();
+ /**
+ * This method submits an HTTP DELETE request against the supplied URL.
+ *
+ * @param url - The REST endpoint to submit the DELETE request to.
+ * @param headers - The headers that should be passed in the request
+ * @param responseType - The expected format of the response.
+ *
+ * @return The result of the DELETE request.
+ */
+ public OperationResult delete(String url, Map<String, List<String>> headers, MediaType responseType) {
+ return processRequest(deleteOp, url, null, headers, null, responseType);
+ }
- }
+ /**
+ * This method does a health check ("ping") against the supplied URL.
+ *
+ * @param url - The REST endpoint to attempt a health check.
+ * @param srcAppName - The name of the application using this client.
+ * @param destAppName - The name of the destination app.
+ *
+ * @return A boolean value. True if connection attempt was successful, false otherwise.
+ *
+ */
+ public boolean healthCheck(String url, String srcAppName, String destAppName) {
+ return healthCheck(url, srcAppName, destAppName, MediaType.TEXT_PLAIN_TYPE);
- /**
- * This method will only be called if computerIfAbsent is true. The return value is null, then the result is not
- * stored in the map.
- *
- * @return a new client instance or null
- */
- private InitializedClient loggedClientInitialization() {
+ }
- if (logger.isDebugEnabled()) {
- logger.debug("Instantiating REST client with following parameters:");
- logger.debug(clientBuilder.toString());
+ /**
+ * This method does a health check ("ping") against the supplied URL.
+ *
+ * @param url - The REST endpoint to attempt a health check.
+ * @param srcAppName - The name of the application using this client.
+ * @param destAppName - The name of the destination app.
+ * @param responseType - The response type.
+ *
+ * @return A boolean value. True if connection attempt was successful, false otherwise.
+ *
+ */
+ public boolean healthCheck(String url, String srcAppName, String destAppName, MediaType responseType) {
+ Map<String, List<String>> headers = new HashMap<>();
+ headers.put(Headers.FROM_APP_ID, Arrays.asList(new String[] {srcAppName}));
+ headers.put(Headers.TRANSACTION_ID, Arrays.asList(new String[] {UUID.randomUUID().toString()}));
+
+ try {
+ logger.info(RestClientMsgs.HEALTH_CHECK_ATTEMPT, destAppName, url);
+ OperationResult result = get(url, headers, responseType);
+
+ if (result != null && result.getFailureCause() == null) {
+ logger.info(RestClientMsgs.HEALTH_CHECK_SUCCESS, destAppName, url);
+ return true;
+ } else {
+ logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url,
+ result != null ? result.getFailureCause() : null);
+ return false;
+ }
+ } catch (Exception e) {
+ logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, e.getMessage());
+ return false;
+ }
}
-
- InitializedClient initClient = new InitializedClient();
-
- try {
- initClient.setClient(clientBuilder.getClient());
- } catch ( Exception error) {
- initClient.setCaughtException(error);
+
+ /**
+ * This method constructs a client request builder that can be used for submitting REST requests to the supplied URL
+ * endpoint.
+ *
+ * @param client - The REST client we will be using to talk to the server.
+ * @param url - The URL endpoint that our request will be submitted to.
+ * @param headers - The headers that should be passed in the request
+ * @param responseType - The expected format of the response.
+ *
+ * @return A client request builder.
+ */
+ private Builder getClientBuilder(Client client, String url, Map<String, List<String>> headers,
+ MediaType responseType) {
+
+ WebTarget target = client.target(url);
+
+ Builder builder = target.request().accept(responseType);
+
+ if (headers != null) {
+ for (Entry<String, List<String>> header : headers.entrySet()) {
+ builder.header(header.getKey(), String.join(";", header.getValue()));
+ }
+
+ // Added additional check to prevent adding duplicate authorization header if client is already sending the
+ // authorization header
+ // AAI-1097 - For AAI calls when Rest authentication mode is selected as SSL_BASIC getting 403 error
+ if (clientBuilder.getAuthenticationMode() == RestAuthenticationMode.SSL_BASIC
+ && headers.get(Headers.AUTHORIZATION) == null) {
+ builder = builder.header(Headers.AUTHORIZATION, clientBuilder.getBasicAuthenticationCredentials());
+ }
+
+ }
+
+ return builder;
}
-
- return initClient;
- }
+ private void debugRequest(String url, String payload, Map<String, List<String>> headers, MediaType responseType) {
+ if (!logger.isDebugEnabled()) {
+ return;
+ }
+
+ StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
+ debugRequest.append("URL: ").append(url).append("\n");
+ debugRequest.append("Payload: ").append(payload).append("\n");
+ debugRequest.append("Response Type: ").append(responseType).append("\n");
+
+ if (headers == null) {
+ logger.debug(debugRequest.toString());
+ return;
+ }
+ debugRequest.append("Headers: ");
+ for (Entry<String, List<String>> header : headers.entrySet()) {
+ debugRequest.append("\n\t").append(header.getKey()).append(":");
+ for (String headerEntry : header.getValue()) {
+ debugRequest.append("\"").append(headerEntry).append("\" ");
+ }
+ }
- /**
- * This method populates the fields of an {@link OperationResult} instance based on the contents
- * of a {@link ClientResponse} received in response to a REST request.
- */
- private void populateOperationResult(ClientResponse response, OperationResult opResult) {
+ logger.debug(debugRequest.toString());
- // If we got back a NULL response, then just produce a generic
- // error code and result indicating this.
- if (response == null) {
- opResult.setResultCode(500);
- opResult.setFailureCause("Client response was null");
- return;
}
-
- int statusCode = response.getStatus();
- opResult.setResultCode(statusCode);
- if (opResult.wasSuccessful()) {
- if (statusCode != Response.Status.NO_CONTENT.getStatusCode()) {
- opResult.setResult(response.getEntity(String.class));
+ private void debugResponse(OperationResult operationResult, MultivaluedMap<String, Object> headers) {
+
+ if (!logger.isDebugEnabled()) {
+ return;
+ }
+
+ StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
+ debugResponse.append("Result: ").append(operationResult.getResultCode()).append("\n");
+ debugResponse.append("Failure Cause: ").append(operationResult.getFailureCause()).append("\n");
+ debugResponse.append("Payload: ").append(operationResult.getResult()).append("\n");
+
+ if (headers == null) {
+ logger.debug(debugResponse.toString());
+ return;
}
- } else {
- opResult.setFailureCause(response.getEntity(String.class));
+
+ debugResponse.append("Headers: ");
+ for (Entry<String, List<Object>> header : headers.entrySet()) {
+ debugResponse.append("\n\t").append(header.getKey()).append(":");
+ for (Object headerEntry : header.getValue()) {
+ debugResponse.append("\"").append(headerEntry).append("\" ");
+ }
+ }
+
+ logger.debug(debugResponse.toString());
}
- opResult.setHeaders(response.getHeaders());
- }
+ /**
+ * This method creates an instance of the low level REST client to use for communicating with the AAI, if one has
+ * not already been created, otherwise it returns the already created instance.
+ *
+ * @return A {@link Client} instance.
+ */
+ protected Client getClient() throws Exception {
+
+ /*
+ * Attempting a new way of doing non-blocking thread-safe lazy-initialization by using Java 1.8 computeIfAbsent
+ * functionality. A null value will not be stored, but once a valid mapping has been established, then the same
+ * value will be returned.
+ *
+ * One awkwardness of the computeIfAbsent is the lack of support for thrown exceptions, which required a bit of
+ * hoop jumping to preserve the original exception for the purpose of maintaining the pre-existing this API
+ * signature.
+ */
+
+ final InitializedClient clientInstance =
+ CLIENT_CACHE.computeIfAbsent(REST_CLIENT_INSTANCE, k -> loggedClientInitialization());
+
+ if (clientInstance.getCaughtException() != null) {
+ throw new InstantiationException(clientInstance.getCaughtException().getMessage());
+ }
+
+ return clientInstance.getClient();
- private class GetRestOperation implements RestOperation {
- public ClientResponse processOperation(Builder builder) {
- return builder.get(ClientResponse.class);
}
- public RequestType getRequestType() {
- return RequestType.GET;
+ /**
+ * This method will only be called if computerIfAbsent is true. The return value is null, then the result is not
+ * stored in the map.
+ *
+ * @return a new client instance or null
+ */
+ private InitializedClient loggedClientInitialization() {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Instantiating REST client with following parameters:");
+ logger.debug(clientBuilder.toString());
+ }
+
+ InitializedClient initClient = new InitializedClient();
+
+ try {
+ initClient.setClient(clientBuilder.getClient());
+ } catch (Exception error) {
+ initClient.setCaughtException(error);
+ }
+
+ return initClient;
+
}
- }
- private class PutRestOperation implements RestOperation {
- public ClientResponse processOperation(Builder builder) {
- return builder.put(ClientResponse.class);
+
+ /**
+ * This method populates the fields of an {@link OperationResult} instance based on the contents of a
+ * {@link Response} received in response to a REST request.
+ */
+ private void populateOperationResult(Response response, OperationResult opResult) {
+
+ // If we got back a NULL response, then just produce a generic
+ // error code and result indicating this.
+ if (response == null) {
+ opResult.setResultCode(500);
+ opResult.setFailureCause("Client response was null");
+ return;
+ }
+
+ int statusCode = response.getStatus();
+ opResult.setResultCode(statusCode);
+
+ if (opResult.wasSuccessful()) {
+ if (statusCode != Response.Status.NO_CONTENT.getStatusCode()) {
+ opResult.setResult(response.readEntity(String.class));
+ }
+ } else {
+ opResult.setFailureCause(response.readEntity(String.class));
+ }
+
+ opResult.setHeaders(convertHeaderObjectsToString(response.getHeaders()));
}
- public RequestType getRequestType() {
- return RequestType.PUT;
+ private MultivaluedMap<String, String> convertHeaderObjectsToString(MultivaluedMap<String, Object> headers) {
+ MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
+ headers.forEach((k, v) -> result.addAll(k, v.stream().map(Object::toString).collect(Collectors.toList())));
+ return result;
}
- }
- private class PostRestOperation implements RestOperation {
- public ClientResponse processOperation(Builder builder) {
- return builder.post(ClientResponse.class);
+ private class GetRestOperation implements RestOperation {
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ return builder.get();
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.GET;
+ }
}
- public RequestType getRequestType() {
- return RequestType.POST;
+ private class PutRestOperation implements RestOperation {
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ return builder.put(Entity.entity(payload, contentType));
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.PUT;
+ }
}
- }
- private class DeleteRestOperation implements RestOperation {
- public ClientResponse processOperation(Builder builder) {
- return builder.delete(ClientResponse.class);
+ private class PostRestOperation implements RestOperation {
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ return builder.post(Entity.entity(payload, contentType));
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.POST;
+ }
}
- public RequestType getRequestType() {
- return RequestType.DELETE;
+ private class DeleteRestOperation implements RestOperation {
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ return builder.delete();
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.DELETE;
+ }
}
- }
-
- private class HeadRestOperation implements RestOperation {
- public ClientResponse processOperation(Builder builder) {
- return builder.head();
+
+ private class HeadRestOperation implements RestOperation {
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ return builder.head();
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.HEAD;
+ }
}
- public RequestType getRequestType() {
- return RequestType.HEAD;
+ private class PatchRestOperation implements RestOperation {
+
+ /**
+ * Technically there is no standarized PATCH operation for the jersey client, but we can use the method-override
+ * approach instead.
+ */
+ @Override
+ public Response processOperation(Builder builder, String payload, MediaType contentType) {
+ builder = builder.header("X-HTTP-Method-Override", "PATCH");
+ return builder.post(Entity.entity(payload, contentType));
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return RequestType.PATCH;
+ }
}
- }
- private class PatchRestOperation implements RestOperation {
/**
- * Technically there is no standarized PATCH operation for the
- * jersey client, but we can use the method-override approach
- * instead.
+ * Interface used wrap a Jersey REST call using a functional interface.
*/
- public ClientResponse processOperation(Builder builder) {
- builder = builder.header("X-HTTP-Method-Override", "PATCH");
- return builder.post(ClientResponse.class);
+ private interface RestOperation {
+
+ /**
+ * Method used to wrap the functionality of making a REST call out to the endpoint.
+ *
+ * @param builder the Jersey builder used to make the request
+ * @param payload the request payload
+ * @param contentType the content type of the payload
+ * @return the response from the REST endpoint
+ */
+ public Response processOperation(Builder builder, String payload, MediaType contentType);
+
+ /**
+ * Returns the REST request type.
+ */
+ public RequestType getRequestType();
+
+ /**
+ * The supported REST request types.
+ */
+ public enum RequestType {
+ GET, PUT, POST, DELETE, PATCH, HEAD
+ }
}
- public RequestType getRequestType() {
- return RequestType.PATCH;
- }
- }
+ /*
+ * An entity to encapsulate an expected result and a potential failure cause when returning from a functional
+ * interface during the computeIfAbsent call.
+ */
+ private class InitializedClient {
+ private Client client;
+ private Throwable caughtException;
+ public InitializedClient() {
+ client = null;
+ caughtException = null;
+ }
- /**
- * Interface used wrap a Jersey REST call using a functional interface.
- */
- private interface RestOperation {
+ public Client getClient() {
+ return client;
+ }
- /**
- * Method used to wrap the functionality of making a REST call out to the endpoint.
- *
- * @param builder the Jersey builder used to make the request
- * @return the response from the REST endpoint
- */
- public ClientResponse processOperation(Builder builder);
+ public void setClient(Client client) {
+ this.client = client;
+ }
- /**
- * Returns the REST request type.
- */
- public RequestType getRequestType();
+ public Throwable getCaughtException() {
+ return caughtException;
+ }
+
+ public void setCaughtException(Throwable caughtException) {
+ this.caughtException = caughtException;
+ }
+
+ }
- /**
- * The supported REST request types.
- */
- public enum RequestType {
- GET, PUT, POST, DELETE, PATCH, HEAD
- }
- }
-
- /*
- * An entity to encapsulate an expected result and a potential failure cause when returning from a
- * functional interface during the computeIfAbsent call.
- */
- private class InitializedClient {
- private Client client;
- private Throwable caughtException;
-
- public InitializedClient() {
- client = null;
- caughtException = null;
- }
-
- public Client getClient() {
- return client;
- }
- public void setClient(Client client) {
- this.client = client;
- }
- public Throwable getCaughtException() {
- return caughtException;
- }
- public void setCaughtException(Throwable caughtException) {
- this.caughtException = caughtException;
- }
-
- }
-
}
diff --git a/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java b/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java
index 26c5fdf..38a8558 100644
--- a/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java
+++ b/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java
@@ -22,281 +22,262 @@ package org.onap.aai.restclient.rest;
import java.io.FileInputStream;
import java.security.KeyStore;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import org.glassfish.jersey.client.ClientProperties;
import org.onap.aai.restclient.enums.RestAuthenticationMode;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-import com.sun.jersey.client.urlconnection.HTTPSProperties;
-
/**
- * This is a generic REST Client builder with flexible security validation. Sometimes it's nice to
- * be able to disable server chain cert validation and hostname validation to work-around lab
- * issues, but at the same time be able to provide complete validation with client cert + hostname +
- * server cert chain validation. I used the ModelLoader REST client as a base and merged in the TSUI
- * client I wrote which also validates the server hostname and server certificate chain.
+ * This is a generic REST Client builder with flexible security validation. Sometimes it's nice to be able to disable
+ * server chain cert validation and hostname validation to work-around lab issues, but at the same time be able to
+ * provide complete validation with client cert + hostname + server cert chain validation. I used the ModelLoader REST
+ * client as a base and merged in the TSUI client I wrote which also validates the server hostname and server
+ * certificate chain.
*/
public class RestClientBuilder {
- public static final boolean DEFAULT_VALIDATE_SERVER_HOST = false;
- public static final boolean DEFAULT_VALIDATE_CERT_CHAIN = false;
- public static final String DEFAULT_CLIENT_CERT_FILENAME = null;
- public static final String DEFAULT_CERT_PASSWORD = null;
- public static final String DEFAULT_TRUST_STORE_FILENAME = null;
- public static final int DEFAULT_CONNECT_TIMEOUT_MS = 60000;
- public static final int DEFAULT_READ_TIMEOUT_MS = 60000;
- public static final RestAuthenticationMode DEFAULT_AUTH_MODE = RestAuthenticationMode.SSL_CERT;
- public static final String DEFAULT_BASIC_AUTH_USERNAME = "";
- public static final String DEFAULT_BASIC_AUTH_PASSWORD = "";
- public static final String DEFAULT_SSL_PROTOCOL = "TLS";
-
- private static final String KEYSTORE_ALGORITHM = "SunX509";
- private static final String KEYSTORE_TYPE = "PKCS12";
- private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
-
- private boolean validateServerHostname;
- private boolean validateServerCertChain;
- private String clientCertFileName;
- private String clientCertPassword;
- private String truststoreFilename;
- private int connectTimeoutInMs;
- private int readTimeoutInMs;
- private RestAuthenticationMode authenticationMode;
- private String basicAuthUsername;
- private String basicAuthPassword;
- private String sslProtocol;
-
- /**
- * Rest Client Builder.
- */
- public RestClientBuilder() {
- validateServerHostname = DEFAULT_VALIDATE_SERVER_HOST;
- validateServerCertChain = DEFAULT_VALIDATE_CERT_CHAIN;
- clientCertFileName = DEFAULT_CLIENT_CERT_FILENAME;
- clientCertPassword = DEFAULT_CERT_PASSWORD;
- truststoreFilename = DEFAULT_TRUST_STORE_FILENAME;
- connectTimeoutInMs = DEFAULT_CONNECT_TIMEOUT_MS;
- readTimeoutInMs = DEFAULT_READ_TIMEOUT_MS;
- authenticationMode = DEFAULT_AUTH_MODE;
- basicAuthUsername = DEFAULT_BASIC_AUTH_USERNAME;
- basicAuthPassword = DEFAULT_BASIC_AUTH_PASSWORD;
- sslProtocol = DEFAULT_SSL_PROTOCOL;
- }
-
- public boolean isValidateServerHostname() {
- return validateServerHostname;
- }
-
- public void setValidateServerHostname(boolean validateServerHostname) {
- this.validateServerHostname = validateServerHostname;
- }
-
- public boolean isValidateServerCertChain() {
- return validateServerCertChain;
- }
-
- public void setValidateServerCertChain(boolean validateServerCertChain) {
- this.validateServerCertChain = validateServerCertChain;
- }
-
- public String getClientCertFileName() {
- return clientCertFileName;
- }
-
- public void setClientCertFileName(String clientCertFileName) {
- this.clientCertFileName = clientCertFileName;
- }
-
- public String getClientCertPassword() {
- return clientCertPassword;
- }
-
- public void setClientCertPassword(String clientCertPassword) {
- this.clientCertPassword = clientCertPassword;
- }
-
- public String getTruststoreFilename() {
- return truststoreFilename;
- }
-
- public void setTruststoreFilename(String truststoreFilename) {
- this.truststoreFilename = truststoreFilename;
- }
-
- public int getConnectTimeoutInMs() {
- return connectTimeoutInMs;
- }
-
- public void setConnectTimeoutInMs(int connectTimeoutInMs) {
- this.connectTimeoutInMs = connectTimeoutInMs;
- }
-
- public int getReadTimeoutInMs() {
- return readTimeoutInMs;
- }
-
- public void setReadTimeoutInMs(int readTimeoutInMs) {
- this.readTimeoutInMs = readTimeoutInMs;
- }
-
- public RestAuthenticationMode getAuthenticationMode() {
- return authenticationMode;
- }
-
- public void setAuthenticationMode(RestAuthenticationMode authenticationMode) {
- this.authenticationMode = authenticationMode;
- }
-
- public String getBasicAuthUsername() {
- return basicAuthUsername;
- }
-
- public void setBasicAuthUsername(String basicAuthUsername) {
- this.basicAuthUsername = basicAuthUsername;
- }
-
- public String getBasicAuthPassword() {
- return basicAuthPassword;
- }
-
- public void setBasicAuthPassword(String basicAuthPassword) {
- this.basicAuthPassword = basicAuthPassword;
- }
-
- public String getSslProtocol() {
- return sslProtocol;
- }
-
- public void setSslProtocol(String sslProtocol) {
- this.sslProtocol = sslProtocol;
- }
-
- /**
- * Returns Client configured for SSL
- */
- public Client getClient() throws Exception {
-
- switch (authenticationMode) {
- case SSL_BASIC:
- case SSL_CERT:
- return getClient(true);
-
- default:
- // return basic non-authenticating HTTP client
- return getClient(false);
+ public static final boolean DEFAULT_VALIDATE_SERVER_HOST = false;
+ public static final boolean DEFAULT_VALIDATE_CERT_CHAIN = false;
+ public static final String DEFAULT_CLIENT_CERT_FILENAME = null;
+ public static final String DEFAULT_CERT_PASSWORD = null;
+ public static final String DEFAULT_TRUST_STORE_FILENAME = null;
+ public static final int DEFAULT_CONNECT_TIMEOUT_MS = 60000;
+ public static final int DEFAULT_READ_TIMEOUT_MS = 60000;
+ public static final RestAuthenticationMode DEFAULT_AUTH_MODE = RestAuthenticationMode.SSL_CERT;
+ public static final String DEFAULT_BASIC_AUTH_USERNAME = "";
+ public static final String DEFAULT_BASIC_AUTH_PASSWORD = "";
+ public static final String DEFAULT_SSL_PROTOCOL = "TLS";
+
+ private static final String KEYSTORE_ALGORITHM = "SunX509";
+ private static final String KEYSTORE_TYPE = "PKCS12";
+ private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+
+ private boolean validateServerHostname;
+ private boolean validateServerCertChain;
+ private String clientCertFileName;
+ private String clientCertPassword;
+ private String truststoreFilename;
+ private int connectTimeoutInMs;
+ private int readTimeoutInMs;
+ private RestAuthenticationMode authenticationMode;
+ private String basicAuthUsername;
+ private String basicAuthPassword;
+ private String sslProtocol;
+
+ /**
+ * Rest Client Builder.
+ */
+ public RestClientBuilder() {
+ validateServerHostname = DEFAULT_VALIDATE_SERVER_HOST;
+ validateServerCertChain = DEFAULT_VALIDATE_CERT_CHAIN;
+ clientCertFileName = DEFAULT_CLIENT_CERT_FILENAME;
+ clientCertPassword = DEFAULT_CERT_PASSWORD;
+ truststoreFilename = DEFAULT_TRUST_STORE_FILENAME;
+ connectTimeoutInMs = DEFAULT_CONNECT_TIMEOUT_MS;
+ readTimeoutInMs = DEFAULT_READ_TIMEOUT_MS;
+ authenticationMode = DEFAULT_AUTH_MODE;
+ basicAuthUsername = DEFAULT_BASIC_AUTH_USERNAME;
+ basicAuthPassword = DEFAULT_BASIC_AUTH_PASSWORD;
+ sslProtocol = DEFAULT_SSL_PROTOCOL;
+ }
+
+ public boolean isValidateServerHostname() {
+ return validateServerHostname;
+ }
+
+ public void setValidateServerHostname(boolean validateServerHostname) {
+ this.validateServerHostname = validateServerHostname;
+ }
+
+ public boolean isValidateServerCertChain() {
+ return validateServerCertChain;
+ }
+
+ public void setValidateServerCertChain(boolean validateServerCertChain) {
+ this.validateServerCertChain = validateServerCertChain;
+ }
+
+ public String getClientCertFileName() {
+ return clientCertFileName;
+ }
+
+ public void setClientCertFileName(String clientCertFileName) {
+ this.clientCertFileName = clientCertFileName;
+ }
+
+ public String getClientCertPassword() {
+ return clientCertPassword;
+ }
+
+ public void setClientCertPassword(String clientCertPassword) {
+ this.clientCertPassword = clientCertPassword;
+ }
+
+ public String getTruststoreFilename() {
+ return truststoreFilename;
+ }
+
+ public void setTruststoreFilename(String truststoreFilename) {
+ this.truststoreFilename = truststoreFilename;
+ }
+
+ public int getConnectTimeoutInMs() {
+ return connectTimeoutInMs;
+ }
+
+ public void setConnectTimeoutInMs(int connectTimeoutInMs) {
+ this.connectTimeoutInMs = connectTimeoutInMs;
}
- }
-
- protected void setupSecureSocketLayerClientConfig(ClientConfig clientConfig) throws Exception {
- // Check to see if we need to perform proper validation of
- // the certificate chains.
- TrustManager[] trustAllCerts = null;
- if (truststoreFilename != null) {
- System.setProperty(TRUST_STORE_PROPERTY, truststoreFilename);
- } else {
- throw new IllegalArgumentException("Trust store filename must be set!");
- }
-
- // Set up the SSL context, keystore, etc. to use for our connection
- // to the AAI.
- SSLContext ctx = SSLContext.getInstance(sslProtocol);
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEYSTORE_ALGORITHM);
- KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);
-
- char[] pwd = null;
- if (clientCertPassword != null) {
- pwd = clientCertPassword.toCharArray();
+ public int getReadTimeoutInMs() {
+ return readTimeoutInMs;
}
- if (clientCertFileName != null) {
- FileInputStream fin = new FileInputStream(clientCertFileName);
+ public void setReadTimeoutInMs(int readTimeoutInMs) {
+ this.readTimeoutInMs = readTimeoutInMs;
+ }
+
+ public RestAuthenticationMode getAuthenticationMode() {
+ return authenticationMode;
+ }
- // Load the keystore and initialize the key manager factory.
- ks.load(fin, pwd);
- kmf.init(ks, pwd);
+ public void setAuthenticationMode(RestAuthenticationMode authenticationMode) {
+ this.authenticationMode = authenticationMode;
+ }
- ctx.init(kmf.getKeyManagers(), trustAllCerts, null);
- } else {
- ctx.init(null, trustAllCerts, null);
+ public String getBasicAuthUsername() {
+ return basicAuthUsername;
}
- // Are we performing validation of the server host name?
- if (validateServerHostname) {
- clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
- new HTTPSProperties(null, ctx));
-
- } else {
- clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
- new HTTPSProperties(new HostnameVerifier() {
- @Override
- public boolean verify(String str, SSLSession sslSession) {
- return true;
- }
- }, ctx));
+ public void setBasicAuthUsername(String basicAuthUsername) {
+ this.basicAuthUsername = basicAuthUsername;
}
- }
+ public String getBasicAuthPassword() {
+ return basicAuthPassword;
+ }
- /**
- * Returns client instance
- *
- * @param useSsl - used to configure the client with an ssl-context or just plain http
- */
- protected Client getClient(boolean useSsl) throws Exception {
+ public void setBasicAuthPassword(String basicAuthPassword) {
+ this.basicAuthPassword = basicAuthPassword;
+ }
- ClientConfig clientConfig = new DefaultClientConfig();
+ public String getSslProtocol() {
+ return sslProtocol;
+ }
- if (useSsl) {
- setupSecureSocketLayerClientConfig(clientConfig);
+ public void setSslProtocol(String sslProtocol) {
+ this.sslProtocol = sslProtocol;
}
- // Finally, create and initialize our client...
- Client client = null;
- client = Client.create(clientConfig);
- client.setConnectTimeout(connectTimeoutInMs);
- client.setReadTimeout(readTimeoutInMs);
-
- // ...and return it to the caller.
- return client;
- }
-
- public String getBasicAuthenticationCredentials() {
-
- String usernameAndPassword = getBasicAuthUsername() + ":" + getBasicAuthPassword();
- return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
- }
-
- /*
- * Added a little bit of logic to obfuscate passwords that could be logged out
- * (non-Javadoc)
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "RestClientBuilder [validateServerHostname=" + validateServerHostname
- + ", validateServerCertChain=" + validateServerCertChain + ", "
- + (clientCertFileName != null ? "clientCertFileName=" + clientCertFileName + ", " : "")
- + (clientCertPassword != null
- ? "clientCertPassword="
- + java.util.Base64.getEncoder().encodeToString(clientCertPassword.getBytes()) + ", "
- : "")
- + (truststoreFilename != null ? "truststoreFilename=" + truststoreFilename + ", " : "")
- + "connectTimeoutInMs=" + connectTimeoutInMs + ", readTimeoutInMs=" + readTimeoutInMs + ", "
- + (authenticationMode != null ? "authenticationMode=" + authenticationMode + ", " : "")
- + (basicAuthUsername != null ? "basicAuthUsername=" + basicAuthUsername + ", " : "")
- + (basicAuthPassword != null ? "basicAuthPassword="
- + java.util.Base64.getEncoder().encodeToString(basicAuthPassword.getBytes()) : "")
- + "]";
- }
+ /**
+ * Returns Client configured for SSL
+ */
+ public Client getClient() throws Exception {
+
+ switch (authenticationMode) {
+ case SSL_BASIC:
+ case SSL_CERT:
+ return getClient(true);
+
+ default:
+ // return basic non-authenticating HTTP client
+ return getClient(false);
+ }
+
+ }
+
+ protected void setupSecureSocketLayerClientConfig(ClientBuilder builder) throws Exception {
+ // Check to see if we need to perform proper validation of
+ // the certificate chains.
+ TrustManager[] trustAllCerts = null;
+ if (truststoreFilename != null) {
+ System.setProperty(TRUST_STORE_PROPERTY, truststoreFilename);
+ } else {
+ throw new IllegalArgumentException("Trust store filename must be set!");
+ }
+
+ // Set up the SSL context, keystore, etc. to use for our connection
+ // to the AAI.
+ SSLContext ctx = SSLContext.getInstance(sslProtocol);
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEYSTORE_ALGORITHM);
+ KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);
+
+ char[] pwd = null;
+ if (clientCertPassword != null) {
+ pwd = clientCertPassword.toCharArray();
+ }
+
+ if (clientCertFileName != null) {
+ FileInputStream fin = new FileInputStream(clientCertFileName);
+
+ // Load the keystore and initialize the key manager factory.
+ ks.load(fin, pwd);
+ kmf.init(ks, pwd);
+
+ ctx.init(kmf.getKeyManagers(), trustAllCerts, null);
+ } else {
+ ctx.init(null, trustAllCerts, null);
+ }
+
+ builder.sslContext(ctx);
+
+ // Are we performing validation of the server host name?
+ if (!validateServerHostname) {
+ builder.hostnameVerifier((String str, SSLSession sslSession) -> true);
+ }
+ }
+
+ /**
+ * Returns client instance
+ *
+ * @param useSsl - used to configure the client with an ssl-context or just plain http
+ */
+ protected Client getClient(boolean useSsl) throws Exception {
+
+ // Finally, create and initialize our client...
+ ClientBuilder builder = ClientBuilder.newBuilder();
+ if (useSsl) {
+ setupSecureSocketLayerClientConfig(builder);
+ }
+ Client client = builder.build();
+ client.property(ClientProperties.CONNECT_TIMEOUT, connectTimeoutInMs);
+ client.property(ClientProperties.READ_TIMEOUT, readTimeoutInMs);
+
+ // ...and return it to the caller.
+ return client;
+ }
+
+ public String getBasicAuthenticationCredentials() {
+
+ String usernameAndPassword = getBasicAuthUsername() + ":" + getBasicAuthPassword();
+ return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
+ }
+
+ /*
+ * Added a little bit of logic to obfuscate passwords that could be logged out (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "RestClientBuilder [validateServerHostname=" + validateServerHostname + ", validateServerCertChain="
+ + validateServerCertChain + ", "
+ + (clientCertFileName != null ? "clientCertFileName=" + clientCertFileName + ", " : "")
+ + (clientCertPassword != null
+ ? "clientCertPassword="
+ + java.util.Base64.getEncoder().encodeToString(clientCertPassword.getBytes()) + ", "
+ : "")
+ + (truststoreFilename != null ? "truststoreFilename=" + truststoreFilename + ", " : "")
+ + "connectTimeoutInMs=" + connectTimeoutInMs + ", readTimeoutInMs=" + readTimeoutInMs + ", "
+ + (authenticationMode != null ? "authenticationMode=" + authenticationMode + ", " : "")
+ + (basicAuthUsername != null ? "basicAuthUsername=" + basicAuthUsername + ", " : "")
+ + (basicAuthPassword != null ? "basicAuthPassword="
+ + java.util.Base64.getEncoder().encodeToString(basicAuthPassword.getBytes()) : "")
+ + "]";
+ }
}
diff --git a/src/main/resources/logging/RESTClientMsgs.properties b/src/main/resources/logging/RESTClientMsgs.properties
index 75d2b8d..2d1ce83 100644
--- a/src/main/resources/logging/RESTClientMsgs.properties
+++ b/src/main/resources/logging/RESTClientMsgs.properties
@@ -44,6 +44,10 @@ HEALTH_CHECK_SUCCESS=\
AC0006I|\
Successfully established connection to {0} at {1}
+HTTP_REQUEST_TIME_WITH_RETRIES=\
+ AC0007I|\
+ {0} request at url = {1} operation time = {2} attempt number = {3}
+
HTTP_REQUEST_INTERRUPTED=\
AC2001E|\
{0} request interrupted while sleeping at url = {1} with cause = {2}