From c782aa5250b32e8aa3c7ecd088f47fd87334a8ef Mon Sep 17 00:00:00 2001 From: "Lee, Tian (tl5884)" Date: Wed, 21 Nov 2018 12:02:49 +0000 Subject: Rewrite rest-client to use Jersey 2.x API This is to fix clashes with the rs-api 2.x jars that Spring Boot requires on the classpath. Change-Id: Ic36cdf0d650f96c54824e2f51c7f8060b2588c66 Issue-ID: AAI-1939 Signed-off-by: Lee, Tian (tl5884) --- README.md | 2 +- pom.xml | 417 +++--- .../org/onap/aai/restclient/client/RestClient.java | 1503 ++++++++++---------- .../aai/restclient/rest/RestClientBuilder.java | 483 +++---- .../resources/logging/RESTClientMsgs.properties | 4 + .../aai/restclient/client/OperationResultTest.java | 7 +- .../aai/restclient/client/RestfulClientTest.java | 87 +- .../aai/restclient/rest/RestClientBuilderTest.java | 429 +++--- 8 files changed, 1431 insertions(+), 1501 deletions(-) diff --git a/README.md b/README.md index cd22ba5..0da4736 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Note, that all of the above configuration parameters are optional and will be se ### Querying The A&AI Once your service has a client instance, it can query the _Active & Available Inventory_ by specifying an HTTP endpoint, headers, and the expected response format: - MultivaluedMap headers = new MultivaluedMapImpl(); + MultivaluedMap headers = new MultivaluedHashMap<>(); headers.put("Accept", Arrays.asList(new String[]{"application/json"})); headers.put("X-FromAppId", Arrays.asList(new String[]{"APP-ID"})); headers.put("X-TransactionId", Arrays.asList(new String[]{UUID.randomUUID().toString()})); diff --git a/pom.xml b/pom.xml index b386cd6..251e63c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,215 +1,212 @@ - - 4.0.0 - - org.onap.oparent - oparent - 1.2.0 - - - org.onap.aai - rest-client - 1.4.0-SNAPSHOT - aai-rest-client - - - google_checks.xml - - java - jacoco - ${project.build.directory}/surefire-reports - ${project.build.directory}/code-coverage/jacoco-ut.exec - false - ${project.version} - - - - - com.sun.jersey - jersey-client - 1.18 - - - - org.onap.aai.logging-service - common-logging - 1.2.2 - - - - org.mockito - mockito-all - 1.10.19 - test - - - - org.powermock - powermock-module-junit4 - 1.6.2 - test - - - - org.powermock - powermock-api-mockito - 1.6.2 - test - - - - org.powermock - powermock-module-javaagent - 1.6.2 - test - - - - org.powermock - powermock-module-junit4-rule-agent - 1.6.2 - test - - - javax.ws.rs - javax.ws.rs-api - 2.0 - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - maven-release-plugin - 2.4.2 - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.8.1 - - - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.6 - - - - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ${onap.nexus.url} - 176c31dfe190a - ecomp-staging - - - - - org.codehaus.mojo - sonar-maven-plugin - 3.2 - - - org.jacoco - jacoco-maven-plugin - 0.7.7.201606060606 - - true - - - - jacoco-initialize-unit-tests - - prepare-agent - - - ${project.build.directory}/coverage-reports/jacoco.exec - - - - - - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - - - checkstyle - - - - - - + + 4.0.0 + + org.onap.oparent + oparent + 1.2.1 + + + org.onap.aai + rest-client + 1.4.0-SNAPSHOT + aai-rest-client + + + google_checks.xml + + java + jacoco + ${project.build.directory}/surefire-reports + ${project.build.directory}/code-coverage/jacoco-ut.exec + false + ${project.version} + + + + + + org.glassfish.jersey.core + jersey-client + 2.27 + + + + org.onap.aai.logging-service + common-logging + 1.2.2 + + + + org.mockito + mockito-all + 1.10.19 + test + + + + org.powermock + powermock-module-junit4 + 1.6.2 + test + + + + org.powermock + powermock-api-mockito + 1.6.2 + test + + + + org.powermock + powermock-module-javaagent + 1.6.2 + test + + + + org.powermock + powermock-module-junit4-rule-agent + 1.6.2 + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + maven-release-plugin + 2.4.2 + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.8.1 + + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.6 + + + + + + com.mycila + license-maven-plugin + 3.0 + +
License.txt
+ + src/main/java/** + src/test/java/** + pom.xml + + true +
+ + + + + check + + validate + + +
+ + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ${onap.nexus.url} + 176c31dfe190a + ecomp-staging + + + + + org.codehaus.mojo + sonar-maven-plugin + 3.2 + + + org.jacoco + jacoco-maven-plugin + 0.7.7.201606060606 + + true + + + + jacoco-initialize-unit-tests + + prepare-agent + + + ${project.build.directory}/coverage-reports/jacoco.exec + + + + + + +
+
+ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + + checkstyle + + + + + +
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 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> 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 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> 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> 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> 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> 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> 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> 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> 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> 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 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> 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> 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> 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> 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 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> headers, + MediaType responseType) { + + WebTarget target = client.target(url); + + Builder builder = target.request().accept(responseType); + + if (headers != null) { + for (Entry> 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> 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> 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 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> 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 convertHeaderObjectsToString(MultivaluedMap headers) { + MultivaluedMap 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} diff --git a/src/test/java/org/onap/aai/restclient/client/OperationResultTest.java b/src/test/java/org/onap/aai/restclient/client/OperationResultTest.java index 6f18f9a..c002b97 100644 --- a/src/test/java/org/onap/aai/restclient/client/OperationResultTest.java +++ b/src/test/java/org/onap/aai/restclient/client/OperationResultTest.java @@ -28,13 +28,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; - import org.junit.Before; import org.junit.Test; -import org.onap.aai.restclient.client.OperationResult; - -import com.sun.jersey.core.util.MultivaluedMapImpl; public class OperationResultTest { @@ -63,7 +60,7 @@ public class OperationResultTest { assertEquals(opResult.getResult(),"no content found"); assertTrue(opResult.wasSuccessful()); - MultivaluedMap multiMap = new MultivaluedMapImpl(); + MultivaluedMap multiMap = new MultivaluedHashMap<>(); multiMap.add("p1","v1"); multiMap.add("p2","v2"); opResult.setHeaders(multiMap); diff --git a/src/test/java/org/onap/aai/restclient/client/RestfulClientTest.java b/src/test/java/org/onap/aai/restclient/client/RestfulClientTest.java index 5eb7f1f..9fe09a1 100644 --- a/src/test/java/org/onap/aai/restclient/client/RestfulClientTest.java +++ b/src/test/java/org/onap/aai/restclient/client/RestfulClientTest.java @@ -1,8 +1,8 @@ /* * ============LICENSE_START=========================================================================================== * Copyright (c) 2017 AT&T Intellectual Property. - * Copyright (c) 2017 Amdocs - * Modification Copyright (c) 2018 IBM. + * Copyright (c) 2017 Amdocs + * Modification Copyright (c) 2018 IBM. * All rights reserved. * ===================================================================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with @@ -14,7 +14,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * ============LICENSE_END================================================== =========================================== - * + * * ECOMP and OpenECOMP are trademarks and service marks of AT&T Intellectual Property. */ package org.onap.aai.restclient.client; @@ -24,63 +24,59 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Invocation.Builder; +import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; - import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.onap.aai.restclient.client.OperationResult; -import org.onap.aai.restclient.client.RestClient; import org.onap.aai.restclient.enums.RestAuthenticationMode; 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.UniformInterfaceException; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.WebResource.Builder; -import com.sun.jersey.core.util.MultivaluedMapImpl; - public class RestfulClientTest { private static final String TEST_URL = "http://localhost:9000/aai/v7"; - private final MultivaluedMapImpl emptyMap = new MultivaluedMapImpl(); + private final MultivaluedMap emptyMap = new MultivaluedHashMap<>(); private RestClientBuilder mockClientBuilder; private Client mockedClient; - private WebResource mockedWebResource; + private WebTarget mockedWebTarget; private Builder mockedBuilder; - private ClientResponse mockedClientResponse; + private Response mockedClientResponse; /** * Test case initialization - * + * * @throws Exception the exception */ - @SuppressWarnings("unchecked") @Before public void init() throws Exception { - mockedClientResponse = Mockito.mock(ClientResponse.class); + mockedClientResponse = Mockito.mock(Response.class); setResponseStatus(Response.Status.OK); - Mockito.when(mockedClientResponse.getHeaders()).thenReturn(emptyMap); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenReturn("hello"); + Mockito.when(mockedClientResponse.getHeaders()).thenReturn(new MultivaluedHashMap<>()); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenReturn("hello"); mockedBuilder = Mockito.mock(Builder.class); - Mockito.when(mockedBuilder.get(Mockito.any(Class.class))).thenReturn(mockedClientResponse); - Mockito.when(mockedBuilder.post(Mockito.any(Class.class))).thenReturn(mockedClientResponse); - Mockito.when(mockedBuilder.put(Mockito.any(Class.class))).thenReturn(mockedClientResponse); - Mockito.when(mockedBuilder.delete(Mockito.any(Class.class))).thenReturn(mockedClientResponse); + Mockito.when(mockedBuilder.get()).thenReturn(mockedClientResponse); + Mockito.when(mockedBuilder.post(Mockito.any())).thenReturn(mockedClientResponse); + Mockito.when(mockedBuilder.put(Mockito.any())).thenReturn(mockedClientResponse); + Mockito.when(mockedBuilder.delete()).thenReturn(mockedClientResponse); Mockito.when(mockedBuilder.head()).thenReturn(mockedClientResponse); + Mockito.when(mockedBuilder.accept(Mockito.any(MediaType.class))).thenReturn(mockedBuilder); - mockedWebResource = Mockito.mock(WebResource.class); - Mockito.when(mockedWebResource.accept(Mockito.anyVararg())).thenReturn(mockedBuilder); + mockedWebTarget = Mockito.mock(WebTarget.class); + Mockito.when(mockedWebTarget.request()).thenReturn(mockedBuilder); mockedClient = Mockito.mock(Client.class); - Mockito.when(mockedClient.resource(Mockito.anyString())).thenReturn(mockedWebResource); + Mockito.when(mockedClient.target(Mockito.anyString())).thenReturn(mockedWebTarget); mockClientBuilder = Mockito.mock(RestClientBuilder.class); Mockito.when(mockClientBuilder.getClient()).thenReturn(mockedClient); @@ -170,24 +166,24 @@ public class RestfulClientTest { assertNull(result.getResult()); assertNull(result.getFailureCause()); } - + @Test public void validateSuccessfulPost_withMultivaluedHeader() throws Exception { RestClient restClient = buildClient(); - MultivaluedMapImpl headerMap = new MultivaluedMapImpl(); - + MultivaluedMap headerMap = new MultivaluedHashMap<>(); + headerMap.add("txnId", "123"); headerMap.add("txnId", "456"); headerMap.add("txnId", "789"); OperationResult result = restClient.post(TEST_URL, "", headerMap, MediaType.APPLICATION_JSON_TYPE, - MediaType.APPLICATION_JSON_TYPE); + MediaType.APPLICATION_JSON_TYPE); - // capture the txnId header from the outgoing request + // capture the txnId header from the outgoing request ArgumentCaptor txnIdHeaderName = ArgumentCaptor.forClass(String.class); ArgumentCaptor txnIdHeaderValue = ArgumentCaptor.forClass(String.class); - + Mockito.verify(mockedBuilder, Mockito.atLeast(1)).header(txnIdHeaderName.capture(), txnIdHeaderValue.capture()); assertEquals("123;456;789", txnIdHeaderValue.getValue()); @@ -220,7 +216,7 @@ public class RestfulClientTest { @Test public void validateResourceNotFoundGet() throws Exception { setResponseStatus(Response.Status.NOT_FOUND); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenReturn("RNF"); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenReturn("RNF"); OperationResult result = buildClient().get(TEST_URL, emptyMap, MediaType.APPLICATION_JSON_TYPE); @@ -249,7 +245,7 @@ public class RestfulClientTest { @Test public void validateHealthCheckFailureWithThrownException() throws Exception { - Mockito.when(mockedBuilder.get(Mockito.any(Class.class))).thenThrow(new IllegalArgumentException("error")); + Mockito.when(mockedBuilder.get()).thenThrow(new IllegalArgumentException("error")); boolean targetServiceHealthy = buildClient().healthCheck("http://localhost:9000/aai/util/echo", "startSerice", "targetService"); @@ -260,7 +256,7 @@ public class RestfulClientTest { @Test public void validateSuccessfulGetWithRetries() throws Exception { Mockito.when(mockedClientResponse.getStatus()).thenReturn(408).thenReturn(Response.Status.OK.getStatusCode()); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenReturn("error").thenReturn("ok"); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenReturn("error").thenReturn("ok"); OperationResult result = buildClient().get(TEST_URL, emptyMap, MediaType.APPLICATION_JSON_TYPE, 3); @@ -273,7 +269,7 @@ public class RestfulClientTest { @Test public void validateFailedGetWithRetriesCausedByResourceNotFound() throws Exception { setResponseStatus(Response.Status.NOT_FOUND); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenReturn("error").thenReturn("ok"); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenReturn("error").thenReturn("ok"); OperationResult result = buildClient().get(TEST_URL, emptyMap, MediaType.APPLICATION_JSON_TYPE, 3); @@ -286,7 +282,7 @@ public class RestfulClientTest { @Test public void validateFailedGetAfterMaxRetries() throws Exception { setResponseStatus(Response.Status.INTERNAL_SERVER_ERROR); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenReturn("error"); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenReturn("error"); OperationResult result = buildClient().get(TEST_URL, emptyMap, MediaType.APPLICATION_JSON_TYPE, 3); @@ -315,7 +311,6 @@ public class RestfulClientTest { assertNull(result.getFailureCause()); } - @Test public void validateSuccessfulHead() throws Exception { OperationResult result = buildClient().head(TEST_URL, emptyMap, MediaType.APPLICATION_JSON_TYPE); @@ -323,7 +318,6 @@ public class RestfulClientTest { assertEquals(Response.Status.OK.getStatusCode(), result.getResultCode()); assertNotNull(result.getResult()); assertNull(result.getFailureCause()); - } @Test @@ -335,12 +329,11 @@ public class RestfulClientTest { assertEquals(Response.Status.OK.getStatusCode(), result.getResultCode()); assertNotNull(result.getResult()); assertNull(result.getFailureCause()); - } @Test public void testGetClient() throws Exception { - RestClientBuilder restClientBuilder= new RestClientBuilder(); + RestClientBuilder restClientBuilder = new RestClientBuilder(); restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_BASIC); restClientBuilder.setTruststoreFilename("truststore"); assertTrue(restClientBuilder.getClient() instanceof Client); @@ -348,7 +341,7 @@ public class RestfulClientTest { /** * Specify the status code of the response object returned by the mocked client - * + * * @param status object storing the status code to mock in the ClientResponse */ private void setResponseStatus(Status status) { @@ -360,9 +353,9 @@ public class RestfulClientTest { */ private void setResponseToNoContent() { setResponseStatus(Response.Status.NO_CONTENT); - // The Jersey client throws an exception when getEntity() is called following a 204 response - UniformInterfaceException uniformInterfaceException = new UniformInterfaceException(mockedClientResponse); - Mockito.when(mockedClientResponse.getEntity(String.class)).thenThrow(uniformInterfaceException); + // The Jersey client throws an exception when readEntity() is called following a 204 response + ProcessingException processingException = new ProcessingException("No content"); + Mockito.when(mockedClientResponse.readEntity(String.class)).thenThrow(processingException); } /** diff --git a/src/test/java/org/onap/aai/restclient/rest/RestClientBuilderTest.java b/src/test/java/org/onap/aai/restclient/rest/RestClientBuilderTest.java index 7155f9a..3878813 100644 --- a/src/test/java/org/onap/aai/restclient/rest/RestClientBuilderTest.java +++ b/src/test/java/org/onap/aai/restclient/rest/RestClientBuilderTest.java @@ -26,243 +26,216 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import javax.ws.rs.client.Client; import org.junit.Before; import org.junit.Test; import org.onap.aai.restclient.enums.RestAuthenticationMode; -import org.onap.aai.restclient.rest.RestClientBuilder; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.client.urlconnection.HTTPSProperties; /** - * This suite of tests is intended to exercise the functionality of the generice REST client - * builder. + * This suite of tests is intended to exercise the functionality of the generice REST client builder. */ public class RestClientBuilderTest { - /** - * Test case initialization - * - * @throws Exception the exception - */ - @Before - public void init() throws Exception { - } - - private String generateAuthorizationHeaderValue(String username, String password) { - String usernameAndPassword = username + ":" + password; - return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes()); - } - - @Test - public void validateAccesors() { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - // test defaults - assertEquals(restClientBuilder.isValidateServerHostname(), RestClientBuilder.DEFAULT_VALIDATE_SERVER_HOST); - assertEquals(restClientBuilder.isValidateServerCertChain(), RestClientBuilder.DEFAULT_VALIDATE_CERT_CHAIN); - assertEquals(restClientBuilder.getClientCertFileName(), RestClientBuilder.DEFAULT_CLIENT_CERT_FILENAME); - assertEquals(restClientBuilder.getClientCertPassword(), RestClientBuilder.DEFAULT_CERT_PASSWORD); - assertEquals(restClientBuilder.getTruststoreFilename(), RestClientBuilder.DEFAULT_TRUST_STORE_FILENAME); - assertEquals(restClientBuilder.getConnectTimeoutInMs(), RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MS); - assertEquals(restClientBuilder.getReadTimeoutInMs(), RestClientBuilder.DEFAULT_READ_TIMEOUT_MS); - assertEquals(restClientBuilder.getAuthenticationMode(), RestClientBuilder.DEFAULT_AUTH_MODE); - assertEquals(restClientBuilder.getBasicAuthUsername(), RestClientBuilder.DEFAULT_BASIC_AUTH_USERNAME); - assertEquals(restClientBuilder.getBasicAuthPassword(), RestClientBuilder.DEFAULT_BASIC_AUTH_PASSWORD); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.UNKNOWN_MODE); - restClientBuilder.setBasicAuthPassword("password"); - restClientBuilder.setBasicAuthUsername("username"); - restClientBuilder.setClientCertFileName("filename"); - restClientBuilder.setClientCertPassword("password"); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setTruststoreFilename("truststore"); - restClientBuilder.setValidateServerCertChain(true); - restClientBuilder.setValidateServerHostname(true); - - assertEquals(restClientBuilder.isValidateServerHostname(), true); - assertEquals(restClientBuilder.isValidateServerCertChain(), true); - assertEquals(restClientBuilder.getClientCertFileName(), "filename"); - assertEquals(restClientBuilder.getClientCertPassword(), "password"); - assertEquals(restClientBuilder.getTruststoreFilename(), "truststore"); - assertEquals(restClientBuilder.getConnectTimeoutInMs(), 12345); - assertEquals(restClientBuilder.getReadTimeoutInMs(), 54321); - assertEquals(restClientBuilder.getAuthenticationMode(), RestAuthenticationMode.UNKNOWN_MODE); - assertEquals(restClientBuilder.getBasicAuthUsername(), "username"); - assertEquals(restClientBuilder.getBasicAuthPassword(), "password"); - - assertEquals(restClientBuilder.getBasicAuthenticationCredentials(), - generateAuthorizationHeaderValue("username", "password")); - - assertTrue(restClientBuilder.toString().contains("RestClientBuilder")); - - } - - @Test - public void validateNoAuthClientCreation() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.HTTP_NOAUTH); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - - Client client = restClientBuilder.getClient(); - assertNotNull(client); - assertNull(client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES)); - } - - - @Test - public void validateUnknownModeCreateNoAuthClient() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.UNKNOWN_MODE); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - - Client client = restClientBuilder.getClient(); - assertNotNull(client); - assertNull(client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES)); - } - - @Test - public void validateBasicAuthSslClient() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_BASIC); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setBasicAuthUsername("username"); - restClientBuilder.setBasicAuthPassword("password"); - restClientBuilder.setTruststoreFilename("truststore"); - - Client client = restClientBuilder.getClient(); - - Object sslPropertiesObj = client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES); - HTTPSProperties sslProps = null; - if ( sslPropertiesObj instanceof HTTPSProperties ) { - sslProps = (HTTPSProperties)sslPropertiesObj; - assertNotNull(sslProps.getHostnameVerifier()); - } else { - fail("Unexpected value for https properties object"); + /** + * Test case initialization + * + * @throws Exception the exception + */ + @Before + public void init() throws Exception {} + + private String generateAuthorizationHeaderValue(String username, String password) { + String usernameAndPassword = username + ":" + password; + return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes()); } - - } - - @Test (expected=IllegalArgumentException.class) - public void validateSslCertClient_noHostOrCertChainValidation() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setValidateServerCertChain(false); - restClientBuilder.setValidateServerHostname(false); - - Client client = restClientBuilder.getClient(); - } - - @Test (expected=IllegalArgumentException.class) - public void validateSslCertClient_hostOnlyValidation() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setValidateServerCertChain(false); - restClientBuilder.setValidateServerHostname(true); - - Client client = restClientBuilder.getClient(); - - } - - @Test - public void validateSslCertClient_certChainOnlyValidation() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setValidateServerCertChain(true); - restClientBuilder.setValidateServerHostname(false); - restClientBuilder.setTruststoreFilename("truststore"); - restClientBuilder.setClientCertPassword(null); - - Client client = restClientBuilder.getClient(); - - Object sslPropertiesObj = client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES); - HTTPSProperties sslProps = null; - if ( sslPropertiesObj instanceof HTTPSProperties ) { - sslProps = (HTTPSProperties)sslPropertiesObj; - assertNotNull(sslProps.getHostnameVerifier()); - } else { - fail("Unexpected value for https properties object"); + + @Test + public void validateAccesors() { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + // test defaults + assertEquals(restClientBuilder.isValidateServerHostname(), RestClientBuilder.DEFAULT_VALIDATE_SERVER_HOST); + assertEquals(restClientBuilder.isValidateServerCertChain(), RestClientBuilder.DEFAULT_VALIDATE_CERT_CHAIN); + assertEquals(restClientBuilder.getClientCertFileName(), RestClientBuilder.DEFAULT_CLIENT_CERT_FILENAME); + assertEquals(restClientBuilder.getClientCertPassword(), RestClientBuilder.DEFAULT_CERT_PASSWORD); + assertEquals(restClientBuilder.getTruststoreFilename(), RestClientBuilder.DEFAULT_TRUST_STORE_FILENAME); + assertEquals(restClientBuilder.getConnectTimeoutInMs(), RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MS); + assertEquals(restClientBuilder.getReadTimeoutInMs(), RestClientBuilder.DEFAULT_READ_TIMEOUT_MS); + assertEquals(restClientBuilder.getAuthenticationMode(), RestClientBuilder.DEFAULT_AUTH_MODE); + assertEquals(restClientBuilder.getBasicAuthUsername(), RestClientBuilder.DEFAULT_BASIC_AUTH_USERNAME); + assertEquals(restClientBuilder.getBasicAuthPassword(), RestClientBuilder.DEFAULT_BASIC_AUTH_PASSWORD); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.UNKNOWN_MODE); + restClientBuilder.setBasicAuthPassword("password"); + restClientBuilder.setBasicAuthUsername("username"); + restClientBuilder.setClientCertFileName("filename"); + restClientBuilder.setClientCertPassword("password"); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setTruststoreFilename("truststore"); + restClientBuilder.setValidateServerCertChain(true); + restClientBuilder.setValidateServerHostname(true); + + assertEquals(restClientBuilder.isValidateServerHostname(), true); + assertEquals(restClientBuilder.isValidateServerCertChain(), true); + assertEquals(restClientBuilder.getClientCertFileName(), "filename"); + assertEquals(restClientBuilder.getClientCertPassword(), "password"); + assertEquals(restClientBuilder.getTruststoreFilename(), "truststore"); + assertEquals(restClientBuilder.getConnectTimeoutInMs(), 12345); + assertEquals(restClientBuilder.getReadTimeoutInMs(), 54321); + assertEquals(restClientBuilder.getAuthenticationMode(), RestAuthenticationMode.UNKNOWN_MODE); + assertEquals(restClientBuilder.getBasicAuthUsername(), "username"); + assertEquals(restClientBuilder.getBasicAuthPassword(), "password"); + + assertEquals(restClientBuilder.getBasicAuthenticationCredentials(), + generateAuthorizationHeaderValue("username", "password")); + + assertTrue(restClientBuilder.toString().contains("RestClientBuilder")); + } - } - - @Test - public void validateSslCertClient_withHostAndCertChainValidation() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setValidateServerCertChain(true); - restClientBuilder.setValidateServerHostname(true); - restClientBuilder.setClientCertPassword("password"); - restClientBuilder.setTruststoreFilename("truststore"); - - Client client = restClientBuilder.getClient(); - - Object sslPropertiesObj = client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES); - HTTPSProperties sslProps = null; - if ( sslPropertiesObj instanceof HTTPSProperties ) { - sslProps = (HTTPSProperties)sslPropertiesObj; - assertNull(sslProps.getHostnameVerifier()); - } else { - fail("Unexpected value for https properties object"); - } } - - @Test (expected=IllegalArgumentException.class) - public void validateSslCertClient_illegalArgumentExceptionWhenTruststoreIsNull() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - - restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); - restClientBuilder.setConnectTimeoutInMs(12345); - restClientBuilder.setReadTimeoutInMs(54321); - restClientBuilder.setValidateServerCertChain(true); - restClientBuilder.setValidateServerHostname(true); - restClientBuilder.setTruststoreFilename(null); - - /* - * Creating the client in this scenario will cause an IllegalArgumentException caused by the - * truststore being null - */ - Client client = restClientBuilder.getClient(); - - } - - @Test - public void validateSslProtocolConfiguration() throws Exception { - - RestClientBuilder restClientBuilder = new RestClientBuilder(); - assertEquals(RestClientBuilder.DEFAULT_SSL_PROTOCOL, restClientBuilder.getSslProtocol()); - - restClientBuilder.setSslProtocol("TLSv1.2"); - assertEquals("TLSv1.2", restClientBuilder.getSslProtocol()); - - } - + + @Test + public void validateNoAuthClientCreation() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.HTTP_NOAUTH); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + + Client client = restClientBuilder.getClient(); + assertNotNull(client); + } + + + @Test + public void validateUnknownModeCreateNoAuthClient() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.UNKNOWN_MODE); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + + Client client = restClientBuilder.getClient(); + assertNotNull(client); + } + + @Test + public void validateBasicAuthSslClient() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_BASIC); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setBasicAuthUsername("username"); + restClientBuilder.setBasicAuthPassword("password"); + restClientBuilder.setTruststoreFilename("truststore"); + + Client client = restClientBuilder.getClient(); + assertNotNull(client.getHostnameVerifier()); + assertEquals("truststore", System.getProperty("javax.net.ssl.trustStore")); + } + + @Test(expected = IllegalArgumentException.class) + public void validateSslCertClient_noHostOrCertChainValidation() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setValidateServerCertChain(false); + restClientBuilder.setValidateServerHostname(false); + + restClientBuilder.getClient(); + } + + @Test(expected = IllegalArgumentException.class) + public void validateSslCertClient_hostOnlyValidation() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setValidateServerCertChain(false); + restClientBuilder.setValidateServerHostname(true); + + restClientBuilder.getClient(); + + } + + @Test + public void validateSslCertClient_certChainOnlyValidation() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setValidateServerCertChain(true); + restClientBuilder.setValidateServerHostname(false); + restClientBuilder.setTruststoreFilename("truststore"); + restClientBuilder.setClientCertPassword(null); + + Client client = restClientBuilder.getClient(); + // TODO + assertNotNull(client.getHostnameVerifier()); + assertEquals("truststore", System.getProperty("javax.net.ssl.trustStore")); + } + + @Test + public void validateSslCertClient_withHostAndCertChainValidation() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setValidateServerCertChain(true); + restClientBuilder.setValidateServerHostname(true); + restClientBuilder.setClientCertPassword("password"); + restClientBuilder.setTruststoreFilename("truststore"); + + Client client = restClientBuilder.getClient(); + // TODO + assertNull(client.getHostnameVerifier()); + assertEquals("truststore", System.getProperty("javax.net.ssl.trustStore")); + } + + @Test(expected = IllegalArgumentException.class) + public void validateSslCertClient_illegalArgumentExceptionWhenTruststoreIsNull() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + + restClientBuilder.setAuthenticationMode(RestAuthenticationMode.SSL_CERT); + restClientBuilder.setConnectTimeoutInMs(12345); + restClientBuilder.setReadTimeoutInMs(54321); + restClientBuilder.setValidateServerCertChain(true); + restClientBuilder.setValidateServerHostname(true); + restClientBuilder.setTruststoreFilename(null); + + /* + * Creating the client in this scenario will cause an IllegalArgumentException caused by the truststore being + * null + */ + restClientBuilder.getClient(); + + } + + @Test + public void validateSslProtocolConfiguration() throws Exception { + + RestClientBuilder restClientBuilder = new RestClientBuilder(); + assertEquals(RestClientBuilder.DEFAULT_SSL_PROTOCOL, restClientBuilder.getSslProtocol()); + + restClientBuilder.setSslProtocol("TLSv1.2"); + assertEquals("TLSv1.2", restClientBuilder.getSslProtocol()); + + } + } -- cgit 1.2.3-korg