2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 Amdocs
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 package org.onap.aai.restclient.client;
23 import java.io.ByteArrayOutputStream;
24 import java.text.SimpleDateFormat;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.Map.Entry;
30 import java.util.UUID;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ConcurrentMap;
33 import java.util.stream.Collectors;
35 import javax.ws.rs.client.Client;
36 import javax.ws.rs.client.ClientBuilder;
37 import javax.ws.rs.client.Entity;
38 import javax.ws.rs.client.Invocation.Builder;
39 import javax.ws.rs.client.WebTarget;
40 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.MultivaluedHashMap;
42 import javax.ws.rs.core.MultivaluedMap;
43 import javax.ws.rs.core.Response;
45 import org.onap.aai.cl.api.LogLine;
46 import org.onap.aai.cl.mdc.MdcContext;
47 import org.onap.aai.cl.mdc.MdcOverride;
48 import org.onap.aai.restclient.enums.RestAuthenticationMode;
49 import org.onap.aai.restclient.logging.RestClientMsgs;
50 import org.onap.aai.restclient.rest.RestClientBuilder;
52 import com.att.eelf.configuration.EELFLogger;
53 import com.att.eelf.configuration.EELFManager;
58 * This class provides a general client implementation that micro services can use for communicating with the endpoints
59 * via their exposed REST interfaces.
63 public class RestClient {
66 * This is a generic builder that is used for constructing the REST client that we will use to communicate with the
69 private RestClientBuilder clientBuilder;
71 private final ConcurrentMap<String, InitializedClient> CLIENT_CACHE = new ConcurrentHashMap<>();
72 private static final String REST_CLIENT_INSTANCE = "REST_CLIENT_INSTANCE";
74 /** Standard logger for producing log statements. */
75 private static EELFLogger logger = EELFManager.getLogger(RestClient.class.getName());
77 /** Standard logger for producing metric statements. */
78 private static EELFLogger metricsLogger = EELFManager.getMetricsLogger();
79 private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
81 /** Reusable function call for GET REST operations. */
82 private final RestOperation getOp = new GetRestOperation();
84 /** Reusable function call for PUT REST operations. */
85 private final RestOperation putOp = new PutRestOperation();
87 /** Reusable function call for POST REST operations. */
88 private final RestOperation postOp = new PostRestOperation();
90 /** Reusable function call for DELETE REST operations. */
91 private final RestOperation deleteOp = new DeleteRestOperation();
93 /** Reusable function call for HEAD REST operations. */
94 private final RestOperation headOp = new HeadRestOperation();
96 /** Reusable function call for PATCH REST operations. */
97 private final RestOperation patchOp = new PatchRestOperation();
100 * Creates a new instance of the {@link RestClient}.
102 public RestClient(ClientBuilder builder) {
103 clientBuilder = new RestClientBuilder(builder);
107 * Creates a new instance of the {@link RestClient} using the supplied {@link RestClientBuilder}.
109 * @param rcBuilder - The REST client builder that this instance of the {@link RestClient} should use.
111 public RestClient(RestClientBuilder rcBuilder) {
112 clientBuilder = rcBuilder;
115 public RestClient authenticationMode(RestAuthenticationMode mode) {
116 logger.debug("Set rest authentication mode= " + mode);
117 clientBuilder.setAuthenticationMode(mode);
121 public RestClient basicAuthUsername(String username) {
122 logger.debug("Set SSL BasicAuth username = " + username);
123 clientBuilder.setBasicAuthUsername(username);
127 public RestClient basicAuthPassword(String password) {
129 * purposely not logging out the password, I guess we could obfuscate it if we really want to see it in the logs
131 clientBuilder.setBasicAuthPassword(password);
136 * Sets the flag to indicate whether or not validation should be performed against the host name of the server we
137 * are trying to communicate with.
139 * @parameter validate - Set to true to enable validation, false to disable
141 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
143 public RestClient validateServerHostname(boolean validate) {
144 logger.debug("Set validate server hostname = " + validate);
145 clientBuilder.setValidateServerHostname(validate);
150 * Sets the flag to indicate whether or not validation should be performed against the certificate chain.
152 * @parameter validate - Set to true to enable validation, false to disable.
154 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
156 public RestClient validateServerCertChain(boolean validate) {
157 logger.debug("Set validate server certificate chain = " + validate);
158 clientBuilder.setValidateServerCertChain(validate);
163 * Assigns the client certificate file to use.
165 * @param filename - The name of the certificate file.
167 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
169 public RestClient clientCertFile(String filename) {
170 logger.debug("Set client certificate filename = " + filename);
171 clientBuilder.setClientCertFileName(filename);
176 * Assigns the client certificate password to use.
178 * @param password - The certificate password.
180 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
182 public RestClient clientCertPassword(String password) {
183 clientBuilder.setClientCertPassword(password);
188 * Assigns the name of the trust store file to use.
190 * @param filename - the name of the trust store file.
192 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
194 public RestClient trustStore(String filename) {
195 logger.debug("Set trust store filename = " + filename);
196 clientBuilder.setTruststoreFilename(filename);
201 * Assigns the connection timeout (in ms) to use when connecting to the target server.
203 * @param timeout - The length of time to wait in ms before timing out.
205 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
207 public RestClient connectTimeoutMs(int timeout) {
208 logger.debug("Set connection timeout = " + timeout + " ms");
209 clientBuilder.setConnectTimeoutInMs(timeout);
214 * Assigns the read timeout (in ms) to use when communicating with the target server.
216 * @param timeout The read timeout in milliseconds.
218 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
220 public RestClient readTimeoutMs(int timeout) {
221 logger.debug("Set read timeout = " + timeout + " ms");
222 clientBuilder.setReadTimeoutInMs(timeout);
227 * Configures the client for a specific SSL protocol
229 * @param sslProtocol - protocol string constant such as TLS, TLSv1, TLSv1.1, TLSv1.2
231 * @return The AAIRESTClient instance.
233 public RestClient sslProtocol(String sslProtocol) {
234 logger.debug("Set sslProtocol = " + sslProtocol);
235 clientBuilder.setSslProtocol(sslProtocol);
239 private boolean shouldRetry(OperationResult operationResult) {
241 if (operationResult == null) {
245 int resultCode = operationResult.getResultCode();
247 if (resultCode == 200) {
251 if (resultCode == 404) {
260 * This method operates on a REST endpoint by submitting an HTTP operation request against the supplied URL. This
261 * variant of the method will perform a requested number of retries in the event that the first request is
264 * @param operation - the REST operation type to send to the url
265 * @param url - The REST endpoint to submit the REST request to.
266 * @param payload - They payload to provide in the REST request, if applicable
267 * @param headers - The headers that should be passed in the request
268 * @param contentType - The content type of the payload
269 * @param responseType - The expected format of the response.
271 * @return The result of the REST request.
273 protected OperationResult processRequest(RestOperation operation, String url, String payload,
274 Map<String, List<String>> headers, MediaType contentType, MediaType responseType, int numRetries) {
277 OperationResult result = null;
279 long startTimeInMs = System.currentTimeMillis();
280 for (int retryCount = 0; retryCount < numRetries; retryCount++) {
282 logger.info(RestClientMsgs.HTTP_REQUEST_WITH_RETRIES, operation.getRequestType().toString(), url,
283 Integer.toString(retryCount + 1));
285 // Submit our query to the AAI.
286 result = processRequest(operation, url, payload, headers, contentType, responseType);
288 // If the submission was successful then we're done.
290 if (!shouldRetry(result)) {
292 logger.info(RestClientMsgs.HTTP_REQUEST_TIME_WITH_RETRIES, operation.getRequestType().toString(), url,
293 Long.toString(System.currentTimeMillis() - startTimeInMs), Integer.toString(retryCount));
295 result.setNumRetries(retryCount);
300 // Our submission was unsuccessful...
302 // Sleep between re-tries to be nice to the target system.
305 } catch (InterruptedException e) {
306 logger.error(RestClientMsgs.HTTP_REQUEST_INTERRUPTED, url, e.getLocalizedMessage());
307 Thread.currentThread().interrupt();
312 // If we've gotten this far, then we failed all of our retries.
313 if (result == null) {
314 result = new OperationResult();
317 result.setNumRetries(numRetries);
318 result.setResultCode(504);
319 result.setFailureCause("Failed to get a successful result after multiple retries to target server.");
326 * This method operates on a REST endpoint by submitting an HTTP operation request against the supplied URL.
328 * @param operation - the REST operation type to send to the url
329 * @param url - The REST endpoint to submit the REST request to.
330 * @param payload - They payload to provide in the REST request, if applicable
331 * @param headers - The headers that should be passed in the request
332 * @param contentType - The content type of the payload
333 * @param responseType - The expected format of the response.
335 * @return The result of the REST request.
337 protected OperationResult processRequest(RestOperation operation, String url, String payload,
338 Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
340 Response clientResponse = null;
341 OperationResult operationResult = new OperationResult();
342 ByteArrayOutputStream baos = new ByteArrayOutputStream();
344 String requestType = operation.getRequestType().name();
346 // Grab the current time so that we can log how long the
347 // query took once we are done.
348 long startTimeInMs = System.currentTimeMillis();
349 MdcOverride override = new MdcOverride();
350 override.addAttribute(MdcContext.MDC_START_TIME, formatter.format(startTimeInMs));
352 logger.info(RestClientMsgs.HTTP_REQUEST, requestType, url);
356 // Get a REST client instance for our request.
357 Client client = getClient();
359 // Debug log the request
360 debugRequest(url, payload, headers, responseType);
362 // Get a client request builder, and submit our GET request.
363 Builder builder = getClientBuilder(client, url, headers, responseType);
364 clientResponse = operation.processOperation(builder, payload, contentType);
366 populateOperationResult(clientResponse, operationResult);
368 // Debug log the response
369 if (clientResponse != null) {
370 debugResponse(operationResult, clientResponse.getHeaders());
373 } catch (Exception ex) {
375 logger.error(RestClientMsgs.HTTP_REQUEST_ERROR, requestType, url, ex.getLocalizedMessage());
376 operationResult.setResultCode(500);
378 .setFailureCause("Error during GET operation to AAI with message = " + ex.getLocalizedMessage());
382 if (logger.isDebugEnabled()) {
383 logger.debug(baos.toString());
386 // Not every valid response code is actually represented by the Response.Status
387 // object, so we need to guard against missing codes, otherwise we throw null
388 // pointer exceptions when we try to generate our metrics logs...
389 Response.Status responseStatus = Response.Status.fromStatusCode(operationResult.getResultCode());
390 String responseStatusCodeString = "";
391 if (responseStatus != null) {
392 responseStatusCodeString = responseStatus.toString();
394 Map<String, String> logFields = new HashMap<String, String>();
395 logFields.put(LogLine.DefinedFields.STATUS_CODE.name(), responseStatusCodeString);
396 logFields.put(LogLine.DefinedFields.RESPONSE_CODE.name(), String.valueOf(operationResult.getResultCode()));
397 logFields.put(LogLine.DefinedFields.RESPONSE_DESCRIPTION.name(), operationResult.getResult());
398 metricsLogger.info(RestClientMsgs.HTTP_REQUEST_TIME, logFields.toString(), requestType, Long.toString(System.currentTimeMillis() - startTimeInMs), url);
399 logger.info(RestClientMsgs.HTTP_REQUEST_TIME, requestType,
400 Long.toString(System.currentTimeMillis() - startTimeInMs), url);
401 logger.info(RestClientMsgs.HTTP_RESPONSE, url,
402 operationResult.getResultCode() + " " + responseStatusCodeString);
405 return operationResult;
409 * This method submits an HTTP PUT request against the supplied URL.
411 * @param url - The REST endpoint to submit the PUT request to.
412 * @param payload - the payload to send to the supplied URL
413 * @param headers - The headers that should be passed in the request
414 * @param contentType - The content type of the payload
415 * @param responseType - The expected format of the response.
417 * @return The result of the PUT request.
419 public OperationResult put(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
420 MediaType responseType) {
421 return processRequest(putOp, url, payload, headers, contentType, responseType);
425 * This method submits an HTTP POST request against the supplied URL.
427 * @param url - The REST endpoint to submit the POST request to.
428 * @param payload - the payload to send to the supplied URL
429 * @param headers - The headers that should be passed in the request
430 * @param contentType - The content type of the payload
431 * @param responseType - The expected format of the response.
433 * @return The result of the POST request.
435 public OperationResult post(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
436 MediaType responseType) {
437 return processRequest(postOp, url, payload, headers, contentType, responseType);
441 * This method submits an HTTP POST request against the supplied URL, and emulates a PATCH operation by setting a
442 * special header value
444 * @param url - The REST endpoint to submit the POST request to.
445 * @param payload - the payload to send to the supplied URL
446 * @param headers - The headers that should be passed in the request
447 * @param contentType - The content type of the payload
448 * @param responseType - The expected format of the response.
450 * @return The result of the PATCH request.
452 public OperationResult patch(String url, String payload, Map<String, List<String>> headers, MediaType contentType,
453 MediaType responseType) {
454 return processRequest(patchOp, url, payload, headers, contentType, responseType);
458 * This method submits an HTTP HEAD request against the supplied URL
460 * @param url - The REST endpoint to submit the POST request to.
461 * @param headers - The headers that should be passed in the request
462 * @param responseType - The expected format of the response.
464 * @return The result of the HEAD request.
466 public OperationResult head(String url, Map<String, List<String>> headers, MediaType responseType) {
467 return processRequest(headOp, url, null, headers, null, responseType);
471 * This method submits an HTTP GET request against the supplied URL.
473 * @param url - The REST endpoint to submit the GET request to.
474 * @param headers - The headers that should be passed in the request
475 * @param responseType - The expected format of the response.
477 * @return The result of the GET request.
479 public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType) {
480 return processRequest(getOp, url, null, headers, null, responseType);
484 * This method submits an HTTP GET request against the supplied URL. This variant of the method will perform a
485 * requested number of retries in the event that the first request is unsuccessful.
487 * @param url - The REST endpoint to submit the GET request to.
488 * @param headers - The headers that should be passed in the request
489 * @param responseType - The expected format of the response.
490 * @param numRetries - The number of times to try resubmitting the request in the event of a failure.
492 * @return The result of the GET request.
494 public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType, int numRetries) {
495 return processRequest(getOp, url, null, headers, null, responseType, numRetries);
499 * This method submits an HTTP DELETE request against the supplied URL.
501 * @param url - The REST endpoint to submit the DELETE request to.
502 * @param headers - The headers that should be passed in the request
503 * @param responseType - The expected format of the response.
505 * @return The result of the DELETE request.
507 public OperationResult delete(String url, Map<String, List<String>> headers, MediaType responseType) {
508 return processRequest(deleteOp, url, null, headers, null, responseType);
512 * This method does a health check ("ping") against the supplied URL.
514 * @param url - The REST endpoint to attempt a health check.
515 * @param srcAppName - The name of the application using this client.
516 * @param destAppName - The name of the destination app.
518 * @return A boolean value. True if connection attempt was successful, false otherwise.
521 public boolean healthCheck(String url, String srcAppName, String destAppName) {
522 return healthCheck(url, srcAppName, destAppName, MediaType.TEXT_PLAIN_TYPE);
527 * This method does a health check ("ping") against the supplied URL.
529 * @param url - The REST endpoint to attempt a health check.
530 * @param srcAppName - The name of the application using this client.
531 * @param destAppName - The name of the destination app.
532 * @param responseType - The response type.
534 * @return A boolean value. True if connection attempt was successful, false otherwise.
537 public boolean healthCheck(String url, String srcAppName, String destAppName, MediaType responseType) {
538 Map<String, List<String>> headers = new HashMap<>();
539 headers.put(Headers.FROM_APP_ID, Arrays.asList(new String[] {srcAppName}));
540 headers.put(Headers.TRANSACTION_ID, Arrays.asList(new String[] {UUID.randomUUID().toString()}));
543 logger.info(RestClientMsgs.HEALTH_CHECK_ATTEMPT, destAppName, url);
544 OperationResult result = get(url, headers, responseType);
546 if (result != null && result.getFailureCause() == null) {
547 logger.info(RestClientMsgs.HEALTH_CHECK_SUCCESS, destAppName, url);
550 logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url,
551 result != null ? result.getFailureCause() : null);
554 } catch (Exception e) {
555 logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, e.getMessage());
561 * This method constructs a client request builder that can be used for submitting REST requests to the supplied URL
564 * @param client - The REST client we will be using to talk to the server.
565 * @param url - The URL endpoint that our request will be submitted to.
566 * @param headers - The headers that should be passed in the request
567 * @param responseType - The expected format of the response.
569 * @return A client request builder.
571 private Builder getClientBuilder(Client client, String url, Map<String, List<String>> headers,
572 MediaType responseType) {
574 WebTarget target = client.target(url);
576 Builder builder = target.request().accept(responseType);
578 if (headers != null) {
579 for (Entry<String, List<String>> header : headers.entrySet()) {
580 builder.header(header.getKey(), String.join(";", header.getValue()));
583 // Added additional check to prevent adding duplicate authorization header if client is already sending the
584 // authorization header
585 // AAI-1097 - For AAI calls when Rest authentication mode is selected as SSL_BASIC getting 403 error
586 if (clientBuilder.getAuthenticationMode() == RestAuthenticationMode.SSL_BASIC
587 && headers.get(Headers.AUTHORIZATION) == null) {
588 builder = builder.header(Headers.AUTHORIZATION, clientBuilder.getBasicAuthenticationCredentials());
596 private void debugRequest(String url, String payload, Map<String, List<String>> headers, MediaType responseType) {
597 if (!logger.isDebugEnabled()) {
601 StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
602 debugRequest.append("URL: ").append(url).append("\n");
603 debugRequest.append("Payload: ").append(payload).append("\n");
604 debugRequest.append("Response Type: ").append(responseType).append("\n");
606 if (headers == null) {
607 logger.debug(debugRequest.toString());
611 debugRequest.append("Headers: ");
612 for (Entry<String, List<String>> header : headers.entrySet()) {
613 debugRequest.append("\n\t").append(header.getKey()).append(":");
614 for (String headerEntry : header.getValue()) {
615 debugRequest.append("\"").append(headerEntry).append("\" ");
619 logger.debug(debugRequest.toString());
623 private void debugResponse(OperationResult operationResult, MultivaluedMap<String, Object> headers) {
625 if (!logger.isDebugEnabled()) {
629 StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
630 debugResponse.append("Result: ").append(operationResult.getResultCode()).append("\n");
631 debugResponse.append("Failure Cause: ").append(operationResult.getFailureCause()).append("\n");
632 debugResponse.append("Payload: ").append(operationResult.getResult()).append("\n");
634 if (headers == null) {
635 logger.debug(debugResponse.toString());
639 debugResponse.append("Headers: ");
640 for (Entry<String, List<Object>> header : headers.entrySet()) {
641 debugResponse.append("\n\t").append(header.getKey()).append(":");
642 for (Object headerEntry : header.getValue()) {
643 debugResponse.append("\"").append(headerEntry).append("\" ");
647 logger.debug(debugResponse.toString());
651 * This method creates an instance of the low level REST client to use for communicating with the AAI, if one has
652 * not already been created, otherwise it returns the already created instance.
654 * @return A {@link Client} instance.
656 protected Client getClient() throws Exception {
659 * Attempting a new way of doing non-blocking thread-safe lazy-initialization by using Java 1.8 computeIfAbsent
660 * functionality. A null value will not be stored, but once a valid mapping has been established, then the same
661 * value will be returned.
663 * One awkwardness of the computeIfAbsent is the lack of support for thrown exceptions, which required a bit of
664 * hoop jumping to preserve the original exception for the purpose of maintaining the pre-existing this API
668 final InitializedClient clientInstance =
669 CLIENT_CACHE.computeIfAbsent(REST_CLIENT_INSTANCE, k -> loggedClientInitialization());
671 if (clientInstance.getCaughtException() != null) {
672 throw new InstantiationException(clientInstance.getCaughtException().getMessage());
675 return clientInstance.getClient();
680 * This method will only be called if computerIfAbsent is true. The return value is null, then the result is not
683 * @return a new client instance or null
685 private InitializedClient loggedClientInitialization() {
687 if (logger.isDebugEnabled()) {
688 logger.debug("Instantiating REST client with following parameters:");
689 logger.debug(clientBuilder.toString());
692 InitializedClient initClient = new InitializedClient();
695 initClient.setClient(clientBuilder.getClient());
696 } catch (Exception error) {
697 initClient.setCaughtException(error);
706 * This method populates the fields of an {@link OperationResult} instance based on the contents of a
707 * {@link Response} received in response to a REST request.
709 private void populateOperationResult(Response response, OperationResult opResult) {
711 // If we got back a NULL response, then just produce a generic
712 // error code and result indicating this.
713 if (response == null) {
714 opResult.setResultCode(500);
715 opResult.setFailureCause("Client response was null");
719 int statusCode = response.getStatus();
720 opResult.setResultCode(statusCode);
722 if (opResult.wasSuccessful()) {
723 if (statusCode != Response.Status.NO_CONTENT.getStatusCode()) {
724 opResult.setResult(response.readEntity(String.class));
727 opResult.setFailureCause(response.readEntity(String.class));
730 opResult.setHeaders(convertHeaderObjectsToString(response.getHeaders()));
733 private MultivaluedMap<String, String> convertHeaderObjectsToString(MultivaluedMap<String, Object> headers) {
734 MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
735 headers.forEach((k, v) -> result.addAll(k, v.stream().map(Object::toString).collect(Collectors.toList())));
739 private class GetRestOperation implements RestOperation {
741 public Response processOperation(Builder builder, String payload, MediaType contentType) {
742 return builder.get();
746 public RequestType getRequestType() {
747 return RequestType.GET;
751 private class PutRestOperation implements RestOperation {
753 public Response processOperation(Builder builder, String payload, MediaType contentType) {
754 return builder.put(Entity.entity(payload, contentType));
758 public RequestType getRequestType() {
759 return RequestType.PUT;
763 private class PostRestOperation implements RestOperation {
765 public Response processOperation(Builder builder, String payload, MediaType contentType) {
766 return builder.post(Entity.entity(payload, contentType));
770 public RequestType getRequestType() {
771 return RequestType.POST;
775 private class DeleteRestOperation implements RestOperation {
777 public Response processOperation(Builder builder, String payload, MediaType contentType) {
778 return builder.delete();
782 public RequestType getRequestType() {
783 return RequestType.DELETE;
787 private class HeadRestOperation implements RestOperation {
789 public Response processOperation(Builder builder, String payload, MediaType contentType) {
790 return builder.head();
794 public RequestType getRequestType() {
795 return RequestType.HEAD;
799 private class PatchRestOperation implements RestOperation {
802 * Technically there is no standarized PATCH operation for the jersey client, but we can use the method-override
806 public Response processOperation(Builder builder, String payload, MediaType contentType) {
807 builder = builder.header("X-HTTP-Method-Override", "PATCH");
808 return builder.post(Entity.entity(payload, contentType));
812 public RequestType getRequestType() {
813 return RequestType.PATCH;
819 * Interface used wrap a Jersey REST call using a functional interface.
821 private interface RestOperation {
824 * Method used to wrap the functionality of making a REST call out to the endpoint.
826 * @param builder the Jersey builder used to make the request
827 * @param payload the request payload
828 * @param contentType the content type of the payload
829 * @return the response from the REST endpoint
831 public Response processOperation(Builder builder, String payload, MediaType contentType);
834 * Returns the REST request type.
836 public RequestType getRequestType();
839 * The supported REST request types.
841 public enum RequestType {
842 GET, PUT, POST, DELETE, PATCH, HEAD
847 * An entity to encapsulate an expected result and a potential failure cause when returning from a functional
848 * interface during the computeIfAbsent call.
850 private class InitializedClient {
851 private Client client;
852 private Throwable caughtException;
854 public InitializedClient() {
856 caughtException = null;
859 public Client getClient() {
863 public void setClient(Client client) {
864 this.client = client;
867 public Throwable getCaughtException() {
868 return caughtException;
871 public void setCaughtException(Throwable caughtException) {
872 this.caughtException = caughtException;