2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017 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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.onap.aai.restclient.client;
25 import java.io.ByteArrayOutputStream;
26 import java.text.SimpleDateFormat;
27 import java.util.Arrays;
28 import java.util.List;
30 import java.util.Map.Entry;
31 import java.util.UUID;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.MultivaluedMap;
37 import javax.ws.rs.core.Response;
39 import org.onap.aai.restclient.enums.RestAuthenticationMode;
40 import org.onap.aai.restclient.logging.RestClientMsgs;
41 import org.onap.aai.restclient.rest.RestClientBuilder;
42 import org.onap.aai.cl.api.LogFields;
43 import org.onap.aai.cl.api.LogLine;
44 import org.onap.aai.cl.api.Logger;
45 import org.onap.aai.cl.eelf.LoggerFactory;
46 import org.onap.aai.cl.mdc.MdcContext;
47 import org.onap.aai.cl.mdc.MdcOverride;
49 import com.sun.jersey.api.client.Client;
50 import com.sun.jersey.api.client.ClientResponse;
51 import com.sun.jersey.api.client.WebResource;
52 import com.sun.jersey.api.client.WebResource.Builder;
53 import com.sun.jersey.core.util.MultivaluedMapImpl;
57 * This class provides a general client implementation that micro services can use for communicating
58 * with the endpoints via their exposed REST interfaces.
62 public class RestClient {
65 * This is a generic builder that is used for constructing the REST client that we will use to
66 * communicate with the REST endpoint.
68 private RestClientBuilder clientBuilder;
70 private final ConcurrentMap<String,InitializedClient> CLIENT_CACHE = new ConcurrentHashMap<String,InitializedClient>();
71 private static final String REST_CLIENT_INSTANCE = "REST_CLIENT_INSTANCE";
73 /** Standard logger for producing log statements. */
74 private Logger logger = LoggerFactory.getInstance().getLogger("AAIRESTClient");
76 /** Standard logger for producing metric statements. */
77 private Logger metricsLogger = LoggerFactory.getInstance().getMetricsLogger("AAIRESTClient");
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();
101 * Creates a new instance of the {@link RestClient}.
103 public RestClient() {
105 clientBuilder = new RestClientBuilder();
111 * Creates a new instance of the {@link RestClient} using the supplied {@link RestClientBuilder}.
113 * @param rcBuilder - The REST client builder that this instance of the {@link RestClient} should
116 public RestClient(RestClientBuilder rcBuilder) {
117 clientBuilder = rcBuilder;
120 public RestClient authenticationMode(RestAuthenticationMode mode) {
121 logger.debug("Set rest authentication mode= " + mode);
122 clientBuilder.setAuthenticationMode(mode);
126 public RestClient basicAuthUsername(String username) {
127 logger.debug("Set SSL BasicAuth username = " + username);
128 clientBuilder.setBasicAuthUsername(username);
132 public RestClient basicAuthPassword(String password) {
134 * purposely not logging out the password, I guess we could obfuscate it if we really want to
137 clientBuilder.setBasicAuthPassword(password);
143 * Sets the flag to indicate whether or not validation should be performed against the host name
144 * of the server we are trying to communicate with.
146 * @parameter validate - Set to true to enable validation, false to disable
148 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
150 public RestClient validateServerHostname(boolean validate) {
151 logger.debug("Set validate server hostname = " + validate);
152 clientBuilder.setValidateServerHostname(validate);
158 * Sets the flag to indicate whether or not validation should be performed against the certificate
161 * @parameter validate - Set to true to enable validation, false to disable.
163 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
165 public RestClient validateServerCertChain(boolean validate) {
166 logger.debug("Set validate server certificate chain = " + validate);
167 clientBuilder.setValidateServerCertChain(validate);
173 * Assigns the client certificate file to use.
175 * @param filename - The name of the certificate file.
177 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
179 public RestClient clientCertFile(String filename) {
180 logger.debug("Set client certificate filename = " + filename);
181 clientBuilder.setClientCertFileName(filename);
187 * Assigns the client certificate password to use.
189 * @param password - The certificate password.
191 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
193 public RestClient clientCertPassword(String password) {
194 clientBuilder.setClientCertPassword(password);
200 * Assigns the name of the trust store file to use.
202 * @param filename - the name of the trust store file.
204 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
206 public RestClient trustStore(String filename) {
207 logger.debug("Set trust store filename = " + filename);
208 clientBuilder.setTruststoreFilename(filename);
214 * Assigns the connection timeout (in ms) to use when connecting to the target server.
216 * @param timeout - The length of time to wait in ms before timing out.
218 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
220 public RestClient connectTimeoutMs(int timeout) {
221 logger.debug("Set connection timeout = " + timeout + " ms");
222 clientBuilder.setConnectTimeoutInMs(timeout);
228 * Assigns the read timeout (in ms) to use when communicating with the target server.
230 * @param timeout The read timeout in milliseconds.
232 * @return The AAIRESTClient instance. This is useful for chaining parameter assignments.
234 public RestClient readTimeoutMs(int timeout) {
235 logger.debug("Set read timeout = " + timeout + " ms");
236 clientBuilder.setReadTimeoutInMs(timeout);
241 * Configures the client for a specific SSL protocol
243 * @param sslProtocol - protocol string constant such as TLS, TLSv1, TLSv1.1, TLSv1.2
245 * @return The AAIRESTClient instance.
247 public RestClient sslProtocol(String sslProtocol) {
248 logger.debug("Set sslProtocol = " + sslProtocol);
249 clientBuilder.setSslProtocol(sslProtocol);
253 private boolean shouldRetry(OperationResult operationResult) {
255 if (operationResult == null) {
259 int resultCode = operationResult.getResultCode();
261 if (resultCode == 200) {
265 if (resultCode == 404) {
274 * This method operates on a REST endpoint by submitting an HTTP operation request against the
276 * This variant of the method will perform a requested number of retries in the event that the
277 * first request is unsuccessful.
279 * @param operation - the REST operation type to send to the url
280 * @param url - The REST endpoint to submit the REST request to.
281 * @param payload - They payload to provide in the REST request, if applicable
282 * @param headers - The headers that should be passed in the request
283 * @param contentType - The content type of the payload
284 * @param responseType - The expected format of the response.
286 * @return The result of the REST request.
288 protected OperationResult processRequest(RestOperation operation, String url, String payload,
289 Map<String, List<String>> headers, MediaType contentType, MediaType responseType,
293 OperationResult result = null;
295 long startTimeInMs = System.currentTimeMillis();
296 for (int retryCount = 0; retryCount < numRetries; retryCount++) {
298 logger.info(RestClientMsgs.HTTP_REQUEST_WITH_RETRIES, operation.getRequestType().toString(),
299 url, Integer.toString(retryCount + 1));
301 // Submit our query to the AAI.
302 result = processRequest(operation, url, payload, headers, contentType, responseType);
304 // If the submission was successful then we're done.
306 if (!shouldRetry(result)) {
308 logger.info(RestClientMsgs.HTTP_REQUEST_TIME_WITH_RETRIES, operation.getRequestType().toString(),url,
309 Long.toString(System.currentTimeMillis() - startTimeInMs),
310 Integer.toString(retryCount));
312 result.setNumRetries(retryCount);
317 // Our submission was unsuccessful...
319 // Sleep between re-tries to be nice to the target system.
322 } catch (InterruptedException e) {
323 logger.error(RestClientMsgs.HTTP_REQUEST_INTERRUPTED, url, e.getLocalizedMessage());
328 // If we've gotten this far, then we failed all of our retries.
329 result.setNumRetries(numRetries);
330 result.setResultCode(504);
331 result.setFailureCause(
332 "Failed to get a successful result after multiple retries to target server.");
338 * This method operates on a REST endpoint by submitting an HTTP operation request against the
341 * @param operation - the REST operation type to send to the url
342 * @param url - The REST endpoint to submit the REST request to.
343 * @param payload - They payload to provide in the REST request, if applicable
344 * @param headers - The headers that should be passed in the request
345 * @param contentType - The content type of the payload
346 * @param responseType - The expected format of the response.
348 * @return The result of the REST request.
350 protected OperationResult processRequest(RestOperation operation, String url, String payload,
351 Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
353 ClientResponse clientResponse = null;
354 OperationResult operationResult = new OperationResult();
355 ByteArrayOutputStream baos = new ByteArrayOutputStream();
357 String requestType = operation.getRequestType().name();
359 // Grab the current time so that we can log how long the
360 // query took once we are done.
361 long startTimeInMs = System.currentTimeMillis();
362 MdcOverride override = new MdcOverride();
363 override.addAttribute(MdcContext.MDC_START_TIME, formatter.format(startTimeInMs));
365 logger.info(RestClientMsgs.HTTP_REQUEST, requestType, url);
369 // Get a REST client instance for our request.
370 Client client = getClient();
372 // Debug log the request
373 debugRequest(url, payload, headers, responseType);
375 // Get a client request builder, and submit our GET request.
376 Builder builder = getClientBuilder(client, url, payload, headers, contentType, responseType);
377 clientResponse = operation.processOperation(builder);
379 populateOperationResult(clientResponse, operationResult);
381 // Debug log the response
382 debugResponse(operationResult, clientResponse.getHeaders());
384 } catch (Exception ex) {
386 logger.error(RestClientMsgs.HTTP_REQUEST_ERROR, requestType, url, ex.getLocalizedMessage());
387 operationResult.setResultCode(500);
388 operationResult.setFailureCause(
389 "Error during GET operation to AAI with message = " + ex.getLocalizedMessage());
393 if (logger.isDebugEnabled()) {
394 logger.debug(baos.toString());
397 // Not every valid response code is actually represented by the Response.Status
398 // object, so we need to guard against missing codes, otherwise we throw null
399 // pointer exceptions when we try to generate our metrics logs...
400 Response.Status responseStatus =
401 Response.Status.fromStatusCode(operationResult.getResultCode());
402 String responseStatusCodeString = "";
403 if (responseStatus != null) {
404 responseStatusCodeString = responseStatus.toString();
407 metricsLogger.info(RestClientMsgs.HTTP_REQUEST_TIME,
408 new LogFields().setField(LogLine.DefinedFields.STATUS_CODE, responseStatusCodeString)
409 .setField(LogLine.DefinedFields.RESPONSE_CODE, operationResult.getResultCode())
410 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, operationResult.getResult()),
411 override, requestType, Long.toString(System.currentTimeMillis() - startTimeInMs), url);
412 logger.info(RestClientMsgs.HTTP_REQUEST_TIME, requestType,
413 Long.toString(System.currentTimeMillis() - startTimeInMs), url);
414 logger.info(RestClientMsgs.HTTP_RESPONSE, url,
415 operationResult.getResultCode() + " " + responseStatusCodeString);
418 return operationResult;
422 * This method submits an HTTP PUT request against the supplied URL.
424 * @param url - The REST endpoint to submit the PUT request to.
425 * @param payload - the payload to send to the supplied URL
426 * @param headers - The headers that should be passed in the request
427 * @param contentType - The content type of the payload
428 * @param responseType - The expected format of the response.
430 * @return The result of the PUT request.
432 public OperationResult put(String url, String payload, Map<String, List<String>> headers,
433 MediaType contentType, MediaType responseType) {
434 return processRequest(putOp, url, payload, headers, contentType, responseType);
438 * This method submits an HTTP POST request against the supplied URL.
440 * @param url - The REST endpoint to submit the POST request to.
441 * @param payload - the payload to send to the supplied URL
442 * @param headers - The headers that should be passed in the request
443 * @param contentType - The content type of the payload
444 * @param responseType - The expected format of the response.
446 * @return The result of the POST request.
448 public OperationResult post(String url, String payload, Map<String, List<String>> headers,
449 MediaType contentType, MediaType responseType) {
450 return processRequest(postOp, url, payload, headers, contentType, responseType);
454 * This method submits an HTTP POST request against the supplied URL, and emulates a PATCH
455 * operation by setting a special header value
457 * @param url - The REST endpoint to submit the POST request to.
458 * @param payload - the payload to send to the supplied URL
459 * @param headers - The headers that should be passed in the request
460 * @param contentType - The content type of the payload
461 * @param responseType - The expected format of the response.
463 * @return The result of the POST request.
465 public OperationResult patch(String url, String payload, Map<String, List<String>> headers,
466 MediaType contentType, MediaType responseType) {
467 return processRequest(patchOp, url, payload, headers, contentType, responseType);
472 * This method submits an HTTP HEAD request against the supplied URL
474 * @param url - The REST endpoint to submit the POST request to.
475 * @param headers - The headers that should be passed in the request
476 * @param responseType - The expected format of the response.
478 * @return The result of the POST request.
480 public OperationResult head(String url, Map<String, List<String>> headers,
481 MediaType responseType) {
482 return processRequest(headOp, url, null, headers, null, responseType);
486 * This method submits an HTTP GET request against the supplied URL.
488 * @param url - The REST endpoint to submit the GET request to.
489 * @param headers - The headers that should be passed in the request
490 * @param responseType - The expected format of the response.
492 * @return The result of the GET request.
494 public OperationResult get(String url, Map<String, List<String>> headers,
495 MediaType responseType) {
496 return processRequest(getOp, url, null, headers, null, responseType);
500 * This method submits an HTTP GET request against the supplied URL.
501 * This variant of the method will perform a requested number of retries in the event that the
502 * first request is unsuccessful.
504 * @param url - The REST endpoint to submit the GET request to.
505 * @param headers - The headers that should be passed in the request
506 * @param responseType - The expected format of the response.
507 * @param numRetries - The number of times to try resubmitting the request in the event of a
510 * @return The result of the GET request.
512 public OperationResult get(String url, Map<String, List<String>> headers, MediaType responseType,
514 return processRequest(getOp, url, null, headers, null, responseType, numRetries);
518 * This method submits an HTTP DELETE request against the supplied URL.
520 * @param url - The REST endpoint to submit the DELETE request to.
521 * @param headers - The headers that should be passed in the request
522 * @param responseType - The expected format of the response.
524 * @return The result of the DELETE request.
526 public OperationResult delete(String url, Map<String, List<String>> headers,
527 MediaType responseType) {
528 return processRequest(deleteOp, url, null, headers, null, responseType);
532 * This method does a health check ("ping") against the supplied URL.
534 * @param url - The REST endpoint to attempt a health check.
535 * @param srcAppName - The name of the application using this client.
536 * @param destAppName - The name of the destination app.
538 * @return A boolean value. True if connection attempt was successful, false otherwise.
541 public boolean healthCheck(String url, String srcAppName, String destAppName) {
542 return healthCheck(url, srcAppName, destAppName, MediaType.TEXT_PLAIN_TYPE);
547 * This method does a health check ("ping") against the supplied URL.
549 * @param url - The REST endpoint to attempt a health check.
550 * @param srcAppName - The name of the application using this client.
551 * @param destAppName - The name of the destination app.
552 * @param responseType - The response type.
554 * @return A boolean value. True if connection attempt was successful, false otherwise.
557 public boolean healthCheck(String url, String srcAppName, String destAppName,
558 MediaType responseType) {
559 MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
560 headers.put(Headers.FROM_APP_ID, Arrays.asList(new String[] {srcAppName}));
561 headers.put(Headers.TRANSACTION_ID, Arrays.asList(new String[] {UUID.randomUUID().toString()}));
564 logger.info(RestClientMsgs.HEALTH_CHECK_ATTEMPT, destAppName, url);
565 OperationResult result = get(url, headers, responseType);
567 if (result != null && result.getFailureCause() == null) {
568 logger.info(RestClientMsgs.HEALTH_CHECK_SUCCESS, destAppName, url);
571 logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url,
572 result.getFailureCause());
575 } catch (Exception e) {
576 logger.error(RestClientMsgs.HEALTH_CHECK_FAILURE, destAppName, url, e.getMessage());
582 * This method constructs a client request builder that can be used for submitting REST requests
583 * to the supplied URL endpoint.
585 * @param client - The REST client we will be using to talk to the server.
586 * @param url - The URL endpoint that our request will be submitted to.
587 * @param headers - The headers that should be passed in the request
588 * @param contentType - the content type of the payload
589 * @param responseType - The expected format of the response.
591 * @return A client request builder.
593 private Builder getClientBuilder(Client client, String url, String payload,
594 Map<String, List<String>> headers, MediaType contentType, MediaType responseType) {
596 WebResource resource = client.resource(url);
597 Builder builder = null;
599 builder = resource.accept(responseType);
601 if (contentType != null) {
602 builder.type(contentType);
605 if (payload != null) {
606 builder.entity(payload);
609 if (headers != null) {
610 for (Entry<String, List<String>> header : headers.entrySet()) {
611 builder.header(header.getKey(), String.join(";",header.getValue()));
614 if (clientBuilder.getAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) {
615 builder = builder.header(Headers.AUTHORIZATION,
616 clientBuilder.getBasicAuthenticationCredentials());
624 private void debugRequest(String url, String payload, Map<String, List<String>> headers,
625 MediaType responseType) {
626 if (logger.isDebugEnabled()) {
627 StringBuilder debugRequest = new StringBuilder("REQUEST:\n");
628 debugRequest.append("URL: ").append(url).append("\n");
629 debugRequest.append("Payload: ").append(payload).append("\n");
630 debugRequest.append("Response Type: ").append(responseType).append("\n");
631 if (headers != null) {
632 debugRequest.append("Headers: ");
633 for (Entry<String, List<String>> header : headers.entrySet()) {
634 debugRequest.append("\n\t").append(header.getKey()).append(":");
635 for (String headerEntry : header.getValue()) {
636 debugRequest.append("\"").append(headerEntry).append("\" ");
640 logger.debug(debugRequest.toString());
644 private void debugResponse(OperationResult operationResult,
645 MultivaluedMap<String, String> headers) {
646 if (logger.isDebugEnabled()) {
647 StringBuilder debugResponse = new StringBuilder("RESPONSE:\n");
648 debugResponse.append("Result: ").append(operationResult.getResultCode()).append("\n");
649 debugResponse.append("Failure Cause: ").append(operationResult.getFailureCause())
651 debugResponse.append("Payload: ").append(operationResult.getResult()).append("\n");
652 if (headers != null) {
653 debugResponse.append("Headers: ");
654 for (Entry<String, List<String>> header : headers.entrySet()) {
655 debugResponse.append("\n\t").append(header.getKey()).append(":");
656 for (String headerEntry : header.getValue()) {
657 debugResponse.append("\"").append(headerEntry).append("\" ");
661 logger.debug(debugResponse.toString());
666 * This method creates an instance of the low level REST client to use for communicating with the
667 * AAI, if one has not already been created, otherwise it returns the already created instance.
669 * @return A {@link Client} instance.
671 protected Client getClient() throws Exception {
674 * Attempting a new way of doing non-blocking thread-safe lazy-initialization by using Java 1.8
675 * computeIfAbsent functionality. A null value will not be stored, but once a valid mapping has
676 * been established, then the same value will be returned.
678 * One awkwardness of the computeIfAbsent is the lack of support for thrown exceptions, which
679 * required a bit of hoop jumping to preserve the original exception for the purpose of
680 * maintaining the pre-existing this API signature.
683 final InitializedClient clientInstance =
684 CLIENT_CACHE.computeIfAbsent(REST_CLIENT_INSTANCE, k -> loggedClientInitialization());
686 if (clientInstance.getCaughtException() != null) {
687 throw new InstantiationException(clientInstance.getCaughtException().getMessage());
690 return clientInstance.getClient();
695 * This method will only be called if computerIfAbsent is true. The return value is null, then the result is not
698 * @return a new client instance or null
700 private InitializedClient loggedClientInitialization() {
702 if (logger.isDebugEnabled()) {
703 logger.debug("Instantiating REST client with following parameters:");
704 logger.debug(clientBuilder.toString());
707 InitializedClient initClient = new InitializedClient();
710 initClient.setClient(clientBuilder.getClient());
711 } catch ( Throwable error ) {
712 initClient.setCaughtException(error);
721 * This method populates the fields of an {@link OperationResult} instance based on the contents
722 * of a {@link ClientResponse} received in response to a REST request.
724 private void populateOperationResult(ClientResponse response, OperationResult opResult) {
726 // If we got back a NULL response, then just produce a generic
727 // error code and result indicating this.
728 if (response == null) {
729 opResult.setResultCode(500);
730 opResult.setFailureCause("Client response was null");
734 int statusCode = response.getStatus();
735 opResult.setResultCode(statusCode);
737 if (opResult.wasSuccessful()) {
738 if (statusCode != Response.Status.NO_CONTENT.getStatusCode()) {
739 opResult.setResult(response.getEntity(String.class));
742 opResult.setFailureCause(response.getEntity(String.class));
745 opResult.setHeaders(response.getHeaders());
748 private class GetRestOperation implements RestOperation {
749 public ClientResponse processOperation(Builder builder) {
750 return builder.get(ClientResponse.class);
753 public RequestType getRequestType() {
754 return RequestType.GET;
758 private class PutRestOperation implements RestOperation {
759 public ClientResponse processOperation(Builder builder) {
760 return builder.put(ClientResponse.class);
763 public RequestType getRequestType() {
764 return RequestType.PUT;
768 private class PostRestOperation implements RestOperation {
769 public ClientResponse processOperation(Builder builder) {
770 return builder.post(ClientResponse.class);
773 public RequestType getRequestType() {
774 return RequestType.POST;
778 private class DeleteRestOperation implements RestOperation {
779 public ClientResponse processOperation(Builder builder) {
780 return builder.delete(ClientResponse.class);
783 public RequestType getRequestType() {
784 return RequestType.DELETE;
788 private class HeadRestOperation implements RestOperation {
789 public ClientResponse processOperation(Builder builder) {
790 return builder.head();
793 public RequestType getRequestType() {
794 return RequestType.HEAD;
798 private class PatchRestOperation implements RestOperation {
801 * Technically there is no standarized PATCH operation for the
802 * jersey client, but we can use the method-override approach
805 public ClientResponse processOperation(Builder builder) {
806 builder = builder.header("X-HTTP-Method-Override", "PATCH");
807 return builder.post(ClientResponse.class);
810 public RequestType getRequestType() {
811 return RequestType.PATCH;
817 * Interface used wrap a Jersey REST call using a functional interface.
819 private interface RestOperation {
822 * Method used to wrap the functionality of making a REST call out to the endpoint.
824 * @param builder the Jersey builder used to make the request
825 * @return the response from the REST endpoint
827 public ClientResponse processOperation(Builder builder);
830 * Returns the REST request type.
832 public RequestType getRequestType();
835 * The supported REST request types.
837 public enum RequestType {
838 GET, PUT, POST, DELETE, PATCH, HEAD
843 * An entity to encapsulate an expected result and a potential failure cause when returning from a
844 * functional interface during the computeIfAbsent call.
846 private class InitializedClient {
847 private Client client;
848 private Throwable caughtException;
850 public InitializedClient() {
852 caughtException = null;
855 public Client getClient() {
858 public void setClient(Client client) {
859 this.client = client;
861 public Throwable getCaughtException() {
862 return caughtException;
864 public void setCaughtException(Throwable caughtException) {
865 this.caughtException = caughtException;