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