diff options
author | dave.adams (da490c) <dave.adams@amdocs.com> | 2017-05-24 17:08:10 -0400 |
---|---|---|
committer | dave.adams (da490c) <dave.adams@amdocs.com> | 2017-05-24 17:53:38 -0400 |
commit | f709acf2e6fc372ed36e0d2612a0b25ff1d582de (patch) | |
tree | 7fe2d851dec0baf8b0018c6bc692bca99fbb22f4 /src/main | |
parent | 80fa392258c5ea6f88e99441630b7c303237b1d1 (diff) |
Update rest-client with additional operations
Change-Id: Ia19c5156d40c816dc1ee77cece92c43f40791c59
Signed-off-by: dave.adams (da490c) <dave.adams@amdocs.com>
Diffstat (limited to 'src/main')
8 files changed, 495 insertions, 106 deletions
diff --git a/src/main/java/org/openecomp/restclient/client/Headers.java b/src/main/java/org/openecomp/restclient/client/Headers.java index aac817e..dbfb6bb 100644 --- a/src/main/java/org/openecomp/restclient/client/Headers.java +++ b/src/main/java/org/openecomp/restclient/client/Headers.java @@ -33,5 +33,5 @@ public final class Headers { 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/openecomp/restclient/client/OperationResult.java b/src/main/java/org/openecomp/restclient/client/OperationResult.java index c9d0f9c..1a301b9 100644 --- a/src/main/java/org/openecomp/restclient/client/OperationResult.java +++ b/src/main/java/org/openecomp/restclient/client/OperationResult.java @@ -28,10 +28,32 @@ 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 MultivaluedMap<String, String> headers;
+ 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.
@@ -39,13 +61,21 @@ public class OperationResult { * @return the HTTP headers of the response.
*/
public MultivaluedMap<String, String> getHeaders() {
- return headers;
+ return responseHeaders;
}
- public void setHeaders(MultivaluedMap<String, String> headers) {
- this.headers = headers;
+ /**
+ * 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;
@@ -62,22 +92,67 @@ public class OperationResult { 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 OperationResult() {
- super();
+ 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 + ", resultCode=" + resultCode + "]";
+ return "OperationResult [result=" + result + ", requestedLink=" + requestedLink
+ + ", failureCause=" + failureCause + ", resultCode=" + resultCode + ", numRetries="
+ + numRetries + ", responseHeaders=" + responseHeaders + "]";
}
}
diff --git a/src/main/java/org/openecomp/restclient/client/RestClient.java b/src/main/java/org/openecomp/restclient/client/RestClient.java index 900c4e0..02d19e6 100644 --- a/src/main/java/org/openecomp/restclient/client/RestClient.java +++ b/src/main/java/org/openecomp/restclient/client/RestClient.java @@ -24,21 +24,6 @@ */ package org.openecomp.restclient.client; -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; - -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 org.openecomp.restclient.logging.RestClientMsgs; -import org.openecomp.restclient.rest.RestClientBuilder; - import java.io.ByteArrayOutputStream; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -46,16 +31,36 @@ 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.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 org.openecomp.restclient.enums.RestAuthenticationMode; +import org.openecomp.restclient.logging.RestClientMsgs; +import org.openecomp.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. + * */ + public class RestClient { /** @@ -63,11 +68,9 @@ public class RestClient { * communicate with the REST endpoint. */ private RestClientBuilder clientBuilder; - - /** - * The low level instance of the REST client that will be used to communicate with the endpoint. - */ - private Client restClient = null; + + 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"); @@ -89,11 +92,20 @@ public class RestClient { /** 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(); + } @@ -106,6 +118,27 @@ public class RestClient { 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; + } /** @@ -160,7 +193,6 @@ public class RestClient { * @return The AAIRESTClient instance. This is useful for chaining parameter assignments. */ public RestClient clientCertPassword(String password) { - logger.debug("Set client certificate password = " + password); clientBuilder.setClientCertPassword(password); return this; } @@ -207,6 +239,26 @@ public class RestClient { 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. @@ -232,23 +284,29 @@ public class RestClient { long startTimeInMs = System.currentTimeMillis(); for (int retryCount = 0; retryCount < numRetries; retryCount++) { - logger.info(RestClientMsgs.HTTP_REQUEST_WITH_RETRIES, url, Integer.toString(retryCount + 1)); - + 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 (Integer.toString(result.getResultCode()).charAt(0) == '2') { - logger.info(RestClientMsgs.HTTP_REQUEST_TIME_WITH_RETRIES, - Long.toString(System.currentTimeMillis() - startTimeInMs), url, + + 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(500); + Thread.sleep(50); } catch (InterruptedException e) { logger.error(RestClientMsgs.HTTP_REQUEST_INTERRUPTED, url, e.getLocalizedMessage()); @@ -257,9 +315,10 @@ public class RestClient { } // 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"); + "Failed to get a successful result after multiple retries to target server."); return result; } @@ -379,7 +438,39 @@ public class RestClient { 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. * @@ -508,6 +599,12 @@ public class RestClient { 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; @@ -554,39 +651,58 @@ public class RestClient { } } - /** * 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. */ - private synchronized Client getClient() throws Exception { + 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()); + } - if (restClient == null) { + return clientInstance.getClient(); - if (logger.isDebugEnabled()) { - logger.debug("Instantiating REST client with following parameters:"); - logger.debug( - " validate server hostname = " + clientBuilder.isValidateServerHostname()); - logger.debug( - " validate server certificate chain = " + clientBuilder.isValidateServerCertChain()); - logger.debug( - " client certificate filename = " + clientBuilder.getClientCertFileName()); - logger.debug( - " client certificate password = " + clientBuilder.getClientCertPassword()); - logger.debug( - " trust store filename = " + clientBuilder.getTruststoreFilename()); - logger.debug(" connection timeout = " - + clientBuilder.getConnectTimeoutInMs() + " ms"); - logger.debug( - " read timeout = " + clientBuilder.getReadTimeoutInMs() + " ms"); - } + } - restClient = clientBuilder.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; - return restClient; } @@ -609,10 +725,10 @@ public class RestClient { opResult.setResultCode(statusCode); - if ((statusCode < 200) || (statusCode > 299)) { - opResult.setFailureCause(payload); - } else { + if (opResult.wasSuccessful()) { opResult.setResult(payload); + } else { + opResult.setFailureCause(payload); } opResult.setHeaders(response.getHeaders()); @@ -657,6 +773,34 @@ public class RestClient { 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. @@ -680,7 +824,36 @@ public class RestClient { * The supported REST request types. */ public enum RequestType { - GET, PUT, POST, DELETE; + 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/openecomp/restclient/enums/RestAuthenticationMode.java b/src/main/java/org/openecomp/restclient/enums/RestAuthenticationMode.java new file mode 100644 index 0000000..5174b75 --- /dev/null +++ b/src/main/java/org/openecomp/restclient/enums/RestAuthenticationMode.java @@ -0,0 +1,66 @@ +/** + * ============LICENSE_START======================================================= + * RestClient + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * All rights reserved. + * ================================================================================ + * 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 and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + */ +package org.openecomp.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/openecomp/restclient/logging/RestClientMsgs.java b/src/main/java/org/openecomp/restclient/logging/RestClientMsgs.java index 0b59139..49ac648 100644 --- a/src/main/java/org/openecomp/restclient/logging/RestClientMsgs.java +++ b/src/main/java/org/openecomp/restclient/logging/RestClientMsgs.java @@ -102,8 +102,13 @@ public enum RestClientMsgs implements LogMessageEnum { * {0} = URL
* {1} - Response code
*/
- HTTP_RESPONSE;
+ HTTP_RESPONSE,
+ /**
+ * . Arguments:
+ * {0} = failure cause
+ */
+ CLIENT_INITIALIZATION_FAILURE;
/**
* Static initializer to ensure the resource bundles for this class are loaded...
diff --git a/src/main/java/org/openecomp/restclient/rest/HttpUtil.java b/src/main/java/org/openecomp/restclient/rest/HttpUtil.java index 89af684..4ea8928 100644 --- a/src/main/java/org/openecomp/restclient/rest/HttpUtil.java +++ b/src/main/java/org/openecomp/restclient/rest/HttpUtil.java @@ -50,7 +50,7 @@ public class HttpUtil { * @return true if the response is of the informational class and false otherwise
*/
public static boolean isHttpResponseClassInformational(int response) {
- return isExpectedHttpResponseClass(response, '1');
+ return ( response >= 100 && response <= 199);
}
/**
@@ -60,7 +60,8 @@ public class HttpUtil { * @return true if the response is of the success class and false otherwise
*/
public static boolean isHttpResponseClassSuccess(int response) {
- return isExpectedHttpResponseClass(response, '2');
+ return ( response >= 200 && response <= 299);
+
}
/**
@@ -70,7 +71,7 @@ public class HttpUtil { * @return true if the response is of the redirection class and false otherwise
*/
public static boolean isHttpResponseClassRedirection(int response) {
- return isExpectedHttpResponseClass(response, '3');
+ return ( response >= 300 && response <= 399);
}
/**
@@ -80,7 +81,7 @@ public class HttpUtil { * @return true if the response is of the client error class and false otherwise
*/
public static boolean isHttpResponseClassClientError(int response) {
- return isExpectedHttpResponseClass(response, '4');
+ return ( response >= 400 && response <= 499);
}
/**
@@ -90,26 +91,7 @@ public class HttpUtil { * @return true if the response is of the server error class and false otherwise
*/
public static boolean isHttpResponseClassServerError(int response) {
- return isExpectedHttpResponseClass(response, '5');
+ return ( response >= 500 && response <= 599);
}
- /**
- * Helper method to determine if we have received the response class we are expecting.
- *
- * @param response the http response we got from our request
- * @param expectedClass the expected http response class ie: 1, 2, 3, 4, 5 which maps to 1xx, 2xx,
- * 3xx, 4xx, 5xx respectively
- * @return true if the response if of our expected class and false if not
- */
- private static boolean isExpectedHttpResponseClass(int response, char expectedClass) {
- if (response < 100 || response >= 600) {
- return false;
- }
-
- if (Integer.toString(response).charAt(0) == expectedClass) {
- return true;
- }
-
- return false;
- }
}
diff --git a/src/main/java/org/openecomp/restclient/rest/RestClientBuilder.java b/src/main/java/org/openecomp/restclient/rest/RestClientBuilder.java index 3d546fe..0afd8a6 100644 --- a/src/main/java/org/openecomp/restclient/rest/RestClientBuilder.java +++ b/src/main/java/org/openecomp/restclient/rest/RestClientBuilder.java @@ -24,11 +24,6 @@ */
package org.openecomp.restclient.rest;
-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;
-
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
@@ -40,16 +35,19 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import org.openecomp.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.
- *
- * @author DAVEA
- *
+ * 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 {
@@ -60,15 +58,14 @@ public class RestClientBuilder { 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.HTTP_NOAUTH;
+ 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";
- /*
- * TODO: implement fluent interface?
- */
-
private boolean validateServerHostname;
private boolean validateServerCertChain;
private String clientCertFileName;
@@ -76,6 +73,9 @@ public class RestClientBuilder { private String truststoreFilename;
private int connectTimeoutInMs;
private int readTimeoutInMs;
+ private RestAuthenticationMode authenticationMode;
+ private String basicAuthUsername;
+ private String basicAuthPassword;
/**
* Rest Client Builder.
@@ -88,6 +88,9 @@ public class RestClientBuilder { truststoreFilename = DEFAULT_TRUST_STORE_FILENAME;
connectTimeoutInMs = DEFAULT_CONNECT_TIMEOUT_MS;
readTimeoutInMs = DEFAULT_READ_TIMEOUT_MS;
+ authenticationMode = RestAuthenticationMode.HTTP_NOAUTH;
+ basicAuthUsername = DEFAULT_BASIC_AUTH_USERNAME;
+ basicAuthPassword = DEFAULT_BASIC_AUTH_PASSWORD;
}
public boolean isValidateServerHostname() {
@@ -146,13 +149,50 @@ public class RestClientBuilder { 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.
+ * Returns Client configured for SSL
*/
public Client getClient() throws Exception {
- ClientConfig clientConfig = new DefaultClientConfig();
+ 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;
@@ -201,7 +241,6 @@ public class RestClientBuilder { ctx.init(null, trustAllCerts, null);
}
-
// Are we performing validation of the server host name?
if (validateServerHostname) {
clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
@@ -216,6 +255,21 @@ public class RestClientBuilder { }
}, 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;
@@ -226,4 +280,34 @@ public class RestClientBuilder { // ...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 9df0764..75d2b8d 100644 --- a/src/main/resources/logging/RESTClientMsgs.properties +++ b/src/main/resources/logging/RESTClientMsgs.properties @@ -55,3 +55,7 @@ HTTP_REQUEST_ERROR=\ HEALTH_CHECK_FAILURE=\ AC2003E|\ Failed to establish connection to {0} at {1}. Cause {2} + +CLIENT_INITIALIZATION_FAILURE=\ + AC2004E|\ + Failure to initialize rest client. Cause {0} |