diff options
author | Arul.Nambi <arul.nambi@amdocs.com> | 2017-09-14 13:44:07 -0400 |
---|---|---|
committer | Arul.Nambi <arul.nambi@amdocs.com> | 2017-09-14 13:46:38 -0400 |
commit | 666b4d7a3b758ebec900f96a6aafe5c1a9ad2093 (patch) | |
tree | 4ac4e8a1180dbe14efac1529035ca507e5c666d5 /src/main/java/org/onap | |
parent | 89ac8a5dd858554fe7f183a114d3b447365c465f (diff) |
Renaming openecomp to onap
Issue-ID: AAI-208
Change-Id: I852fda30e0b44e73da8ae3e4748b49c663e544d0
Signed-off-by: Arul.Nambi <arul.nambi@amdocs.com>
Diffstat (limited to 'src/main/java/org/onap')
7 files changed, 1636 insertions, 0 deletions
diff --git a/src/main/java/org/onap/aai/restclient/client/Headers.java b/src/main/java/org/onap/aai/restclient/client/Headers.java new file mode 100644 index 0000000..56903bc --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/client/Headers.java @@ -0,0 +1,35 @@ +/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.restclient.client;
+
+public final class Headers {
+
+ public static final String FROM_APP_ID = "X-FromAppId";
+ public static final String TRANSACTION_ID = "X-TransactionId";
+ public static final String RESOURCE_VERSION = "resourceVersion";
+ public static final String ETAG = "ETag";
+ public static final String IF_MATCH = "If-Match";
+ public static final String IF_NONE_MATCH = "If-None-Match";
+ public static final String ACCEPT = "Accept";
+ public static final String AUTHORIZATION = "Authorization";
+}
diff --git a/src/main/java/org/onap/aai/restclient/client/OperationResult.java b/src/main/java/org/onap/aai/restclient/client/OperationResult.java new file mode 100644 index 0000000..ad11863 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/client/OperationResult.java @@ -0,0 +1,156 @@ +/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.restclient.client;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+public class OperationResult {
+
+ private String requestedLink;
+ private String result;
+ private String failureCause;
+ private boolean fromCache;
+ private int resultCode;
+ private int numRetries;
+ private MultivaluedMap<String, String> responseHeaders;
+
+
+ public OperationResult() {
+ super();
+ this.numRetries = 0;
+ this.fromCache = false;
+ }
+
+ /**
+ * Instantiates a new operation result.
+ *
+ * @param resultCode the result code
+ * @param result the result
+ */
+ public OperationResult(int resultCode, String result) {
+ this();
+ this.resultCode = resultCode;
+ this.result = result;
+ }
+
+ /**
+ * Get the HTTP headers of the response.
+ *
+ * @return the HTTP headers of the response.
+ */
+ public MultivaluedMap<String, String> getHeaders() {
+ return responseHeaders;
+ }
+
+ /**
+ * Returns true if the HTTP Status Code 200 <= x <= 299
+ *
+ * @return true, if successful
+ */
+ public boolean wasSuccessful() {
+ return (resultCode > 199 && resultCode < 300);
+ }
+
+ public void setHeaders(MultivaluedMap<String, String> headers) {
+ this.responseHeaders = headers;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(String result) {
+ this.result = result;
+ }
+
+ public int getResultCode() {
+ return resultCode;
+ }
+
+ public String getFailureCause() {
+ return failureCause;
+ }
+
+ /**
+ * Sets the result.
+ *
+ * @param resultCode the result code
+ * @param result the result
+ */
+ public void setResult(int resultCode, String result) {
+ this.resultCode = resultCode;
+ this.result = result;
+ }
+
+ public void setFailureCause(String failureCause) {
+ this.failureCause = failureCause;
+ }
+
+ /**
+ * Sets the failure cause.
+ *
+ * @param resultCode the result code
+ * @param failureCause the result error
+ */
+ public void setFailureCause(int resultCode, String failureCause) {
+ this.resultCode = resultCode;
+ this.failureCause = failureCause;
+ }
+
+
+ public void setResultCode(int resultCode) {
+ this.resultCode = resultCode;
+ }
+
+ public String getRequestedLink() {
+ return requestedLink;
+ }
+
+ public void setRequestedLink(String requestedLink) {
+ this.requestedLink = requestedLink;
+ }
+
+ public boolean isFromCache() {
+ return fromCache;
+ }
+
+ public void setFromCache(boolean fromCache) {
+ this.fromCache = fromCache;
+ }
+
+ public int getNumRetries() {
+ return numRetries;
+ }
+
+ public void setNumRetries(int numRetries) {
+ this.numRetries = numRetries;
+ }
+
+ @Override
+ public String toString() {
+ return "OperationResult [result=" + result + ", requestedLink=" + requestedLink
+ + ", failureCause=" + failureCause + ", resultCode=" + resultCode + ", numRetries="
+ + numRetries + ", responseHeaders=" + responseHeaders + "]";
+ }
+
+}
diff --git a/src/main/java/org/onap/aai/restclient/client/RestClient.java b/src/main/java/org/onap/aai/restclient/client/RestClient.java new file mode 100644 index 0000000..02f12b1 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/client/RestClient.java @@ -0,0 +1,857 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.restclient.client; + +import java.io.ByteArrayOutputStream; +import java.text.SimpleDateFormat; +import java.util.Arrays; +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 javax.ws.rs.core.MediaType; +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.openecomp.cl.api.LogFields; +import org.openecomp.cl.api.LogLine; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.cl.mdc.MdcContext; +import org.openecomp.cl.mdc.MdcOverride; + +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. + * + */ + +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<String,InitializedClient>(); + 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 + */ + 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; + } + + 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; + } + + // 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()); + break; + } + } + + // If we've gotten this far, then we failed all of our retries. + 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 + 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.getFailureCause()); + return false; + } + } catch (Exception e) { + logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, e.getMessage()); + return false; + } + } + + /** + * 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) { + + WebResource resource = client.resource(url); + Builder builder = null; + + builder = resource.accept(responseType); + + if (contentType != null) { + builder.type(contentType); + } + + if (payload != null) { + builder.entity(payload); + } + + if (headers != null) { + for (Entry<String, List<String>> header : headers.entrySet()) { + builder.header(header.getKey(), header.getValue()); + } + + if (clientBuilder.getAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) { + builder = builder.header(Headers.AUTHORIZATION, + clientBuilder.getBasicAuthenticationCredentials()); + } + + } + + return builder; + } + + private void debugRequest(String url, String payload, Map<String, List<String>> headers, + MediaType responseType) { + if (logger.isDebugEnabled()) { + 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) { + 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("\" "); + } + } + } + logger.debug(debugRequest.toString()); + } + } + + private void debugResponse(OperationResult operationResult, + MultivaluedMap<String, String> headers) { + if (logger.isDebugEnabled()) { + 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) { + 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("\" "); + } + } + } + logger.debug(debugResponse.toString()); + } + } + + /** + * 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(); + + } + + /** + * 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 ( Throwable error ) { + initClient.setCaughtException(error); + } + + return initClient; + + } + + + /** + * 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) { + + // 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)); + } + } else { + opResult.setFailureCause(response.getEntity(String.class)); + } + + opResult.setHeaders(response.getHeaders()); + } + + private class GetRestOperation implements RestOperation { + public ClientResponse processOperation(Builder builder) { + return builder.get(ClientResponse.class); + } + + public RequestType getRequestType() { + return RequestType.GET; + } + } + + private class PutRestOperation implements RestOperation { + public ClientResponse processOperation(Builder builder) { + return builder.put(ClientResponse.class); + } + + public RequestType getRequestType() { + return RequestType.PUT; + } + } + + private class PostRestOperation implements RestOperation { + public ClientResponse processOperation(Builder builder) { + return builder.post(ClientResponse.class); + } + + public RequestType getRequestType() { + return RequestType.POST; + } + } + + private class DeleteRestOperation implements RestOperation { + public ClientResponse processOperation(Builder builder) { + return builder.delete(ClientResponse.class); + } + + public RequestType getRequestType() { + return RequestType.DELETE; + } + } + + private class HeadRestOperation implements RestOperation { + public ClientResponse processOperation(Builder builder) { + return builder.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. + */ + public ClientResponse processOperation(Builder builder) { + builder = builder.header("X-HTTP-Method-Override", "PATCH"); + return builder.post(ClientResponse.class); + } + + public RequestType getRequestType() { + return RequestType.PATCH; + } + } + + + /** + * Interface used wrap a Jersey REST call using a functional interface. + */ + 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 + * @return the response from the REST endpoint + */ + public ClientResponse processOperation(Builder builder); + + /** + * Returns the REST request type. + */ + public RequestType getRequestType(); + + /** + * 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/enums/RestAuthenticationMode.java b/src/main/java/org/onap/aai/restclient/enums/RestAuthenticationMode.java new file mode 100644 index 0000000..6bff196 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/enums/RestAuthenticationMode.java @@ -0,0 +1,64 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.restclient.enums; + +/** + * Authentication Modes: + * <li>HTTP_NOAUTH - intended to represent basic HTTP no authentication + * <li>SSL_BASIC - HTTP/S with username/password + * <li>SSL_CERT - HTTP/S with client cert + */ + +public enum RestAuthenticationMode { + HTTP_NOAUTH("HTTP_NO_AUTH"), SSL_BASIC("SSL_BASIC"), SSL_CERT("SSL_CERT"), UNKNOWN_MODE( + "UNKNOWN_MODE"); + + private String authenticationModeLabel; + + private RestAuthenticationMode(String authModelLabel) { + this.authenticationModeLabel = authModelLabel; + } + + public String getAuthenticationModeLabel() { + return authenticationModeLabel; + } + + public static RestAuthenticationMode getRestAuthenticationMode(String authenticationMode) { + + RestAuthenticationMode mappedMode = RestAuthenticationMode.UNKNOWN_MODE; + + if (authenticationMode == null) { + return mappedMode; + } + + try { + mappedMode = RestAuthenticationMode.valueOf(authenticationMode); + } catch (Exception exc) { + mappedMode = RestAuthenticationMode.UNKNOWN_MODE; + } + + return mappedMode; + + } + +} diff --git a/src/main/java/org/onap/aai/restclient/logging/RestClientMsgs.java b/src/main/java/org/onap/aai/restclient/logging/RestClientMsgs.java new file mode 100644 index 0000000..09af107 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/logging/RestClientMsgs.java @@ -0,0 +1,117 @@ +/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.restclient.logging;
+
+import com.att.eelf.i18n.EELFResourceManager;
+import org.openecomp.cl.eelf.LogMessageEnum;
+
+public enum RestClientMsgs implements LogMessageEnum {
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} = URL
+ */
+ HTTP_REQUEST,
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} = URL
+ * {2} = Attempt count.
+ */
+ HTTP_REQUEST_WITH_RETRIES,
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} - URL
+ * {2} - Operation time in ms.
+ */
+ HTTP_REQUEST_TIME,
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} - URL
+ * {2} - Operation time in ms.
+ * {3} - Retry count.
+ */
+ HTTP_REQUEST_TIME_WITH_RETRIES,
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} - URL
+ * {2} - Error message.
+ */
+ HTTP_REQUEST_INTERRUPTED,
+
+ /**
+ * Arguments:
+ * {0} = HTTP operation
+ * {1} - URL
+ * {2} - Error message.
+ */
+ HTTP_REQUEST_ERROR,
+
+ /**
+ * . Arguments:
+ * {0} = Target URL
+ */
+ HEALTH_CHECK_ATTEMPT,
+
+ /**
+ * . Arguments:
+ * {0} = Target URL
+ */
+ HEALTH_CHECK_SUCCESS,
+
+ /**
+ * . Arguments:
+ * {0} = Target URL
+ * {1} = failure cause
+ */
+ HEALTH_CHECK_FAILURE,
+
+
+ /**
+ * . Arguments:
+ * {0} = URL
+ * {1} - Response code
+ */
+ HTTP_RESPONSE,
+
+ /**
+ * . Arguments:
+ * {0} = failure cause
+ */
+ CLIENT_INITIALIZATION_FAILURE;
+
+ /**
+ * Static initializer to ensure the resource bundles for this class are loaded...
+ */
+ static {
+ EELFResourceManager.loadMessageBundle("logging/RESTClientMsgs");
+ }
+}
diff --git a/src/main/java/org/onap/aai/restclient/rest/HttpUtil.java b/src/main/java/org/onap/aai/restclient/rest/HttpUtil.java new file mode 100644 index 0000000..0677a16 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/rest/HttpUtil.java @@ -0,0 +1,95 @@ +/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.onap.aai.restclient.rest;
+
+public class HttpUtil {
+
+ /**
+ * Determines if the provided HTTP response is present in the provided list of acceptable response
+ * codes.
+ *
+ * @param response the http response we got from our request
+ * @param list the list of acceptable response codes
+ * @return true if the http response is in the provided list
+ */
+ public static boolean isHttpResponseInList(int response, int... list) {
+ for (int checkCode : list) {
+ if (checkCode == response) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided http response is of the information class.
+ *
+ * @param response the http response we got from our request
+ * @return true if the response is of the informational class and false otherwise
+ */
+ public static boolean isHttpResponseClassInformational(int response) {
+ return ( response >= 100 && response <= 199);
+ }
+
+ /**
+ * Determines if the provided http response is of the success class.
+ *
+ * @param response the http response we got from our request
+ * @return true if the response is of the success class and false otherwise
+ */
+ public static boolean isHttpResponseClassSuccess(int response) {
+ return ( response >= 200 && response <= 299);
+
+ }
+
+ /**
+ * Determines if the provided http response is of the redirection class.
+ *
+ * @param response the http response we got from our request
+ * @return true if the response is of the redirection class and false otherwise
+ */
+ public static boolean isHttpResponseClassRedirection(int response) {
+ return ( response >= 300 && response <= 399);
+ }
+
+ /**
+ * Determines if the provided http response is of the client error class.
+ *
+ * @param response the http response we got from our request
+ * @return true if the response is of the client error class and false otherwise
+ */
+ public static boolean isHttpResponseClassClientError(int response) {
+ return ( response >= 400 && response <= 499);
+ }
+
+ /**
+ * Determines if the provided http response is of the server error class.
+ *
+ * @param response the http response we got from our request
+ * @return true if the response is of the server error class and false otherwise
+ */
+ public static boolean isHttpResponseClassServerError(int response) {
+ return ( response >= 500 && response <= 599);
+ }
+
+}
diff --git a/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java b/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java new file mode 100644 index 0000000..f446f27 --- /dev/null +++ b/src/main/java/org/onap/aai/restclient/rest/RestClientBuilder.java @@ -0,0 +1,312 @@ +/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 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=========================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+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 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.
+ */
+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 = "";
+
+ private static final String 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;
+
+ /**
+ * 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;
+ }
+
+ 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;
+ }
+
+ /**
+ * 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(ClientConfig clientConfig) throws Exception {
+ // Check to see if we need to perform proper validation of
+ // the certificate chains.
+ TrustManager[] trustAllCerts = null;
+ if (validateServerCertChain) {
+ if (truststoreFilename != null) {
+ System.setProperty(TRUST_STORE_PROPERTY, truststoreFilename);
+ } else {
+ throw new IllegalArgumentException("Trust store filename must be set!");
+ }
+
+ } else {
+
+ // We aren't validating certificates, so create a trust manager that does
+ // not validate certificate chains.
+ trustAllCerts = new TrustManager[] {new X509TrustManager() {
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ public void checkClientTrusted(X509Certificate[] certs, String authType) {}
+
+ public void checkServerTrusted(X509Certificate[] certs, String authType) {}
+ }};
+ }
+
+ // Set up the SSL context, keystore, etc. to use for our connection
+ // to the AAI.
+ SSLContext ctx = SSLContext.getInstance(SSL_PROTOCOL);
+ 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);
+ }
+
+ // 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));
+ }
+ }
+
+
+ /**
+ * 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 {
+
+ ClientConfig clientConfig = new DefaultClientConfig();
+
+ if (useSsl) {
+ setupSecureSocketLayerClientConfig(clientConfig);
+ }
+
+ // 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()) : "")
+ + "]";
+ }
+
+}
|