X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=vid-app-common%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fvid%2Fclient%2FSyncRestClient.java;h=5c65c8af45f4be632d012e25975c9303b96a9437;hb=HEAD;hp=b7f22640082f9b3e82fe8be35b459d139de28801;hpb=7b3b0ccf0ec6f62d5ab43992f6485335f4fcb272;p=vid.git diff --git a/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java b/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java index b7f226400..24a78af47 100644 --- a/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java +++ b/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java @@ -2,14 +2,15 @@ * ============LICENSE_START======================================================= * VID * ================================================================================ - * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 - 2019 Nokia. All rights reserved. + * Modifications Copyright (C) 2017 - 2019 AT&T Intellectual Property. 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. @@ -20,128 +21,213 @@ package org.onap.vid.client; +import static org.onap.vid.client.UnirestPatchKt.patched; +import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER; + +import com.att.eelf.configuration.EELFLogger; import io.joshworks.restclient.http.HttpResponse; import io.joshworks.restclient.http.JsonNode; import io.joshworks.restclient.http.RestClient; import io.joshworks.restclient.http.exceptions.RestClientException; import io.joshworks.restclient.http.mapper.ObjectMapper; -import org.apache.http.impl.client.CloseableHttpClient; -import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustSelfSignedStrategy; -import org.eclipse.jetty.util.security.Password; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.conn.ssl.SSLContexts; -import io.vavr.CheckedFunction1; -import lombok.SneakyThrows; -import lombok.val; - -import java.security.UnrecoverableKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.KeyManagementException; -import java.security.cert.CertificateException; -import javax.net.ssl.SSLException; -import java.security.KeyStoreException; -import java.text.SimpleDateFormat; -import javax.net.ssl.SSLContext; +import io.joshworks.restclient.request.GetRequest; +import java.io.File; import java.io.FileInputStream; -import java.security.KeyStore; -import java.text.DateFormat; -import java.io.InputStream; import java.io.IOException; -import java.util.Date; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Map; -import java.io.File; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.eclipse.jetty.util.security.Password; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; import org.onap.portalsdk.core.util.SystemProperties; +import org.onap.vid.logging.ApacheClientMetricRequestInterceptor; +import org.onap.vid.logging.ApacheClientMetricResponseInterceptor; import org.onap.vid.properties.VidProperties; +import org.onap.vid.utils.Logging; +import org.springframework.http.HttpMethod; public class SyncRestClient implements SyncRestClientInterface { - - private static final String CANNOT_INITIALIZE_CUSTOM_HTTP_CLIENT = "Cannot initialize custom http client from current configuration. Using default one."; - private static final String TRY_TO_CALL_OVER_HTTP = "SSL Handshake problem occured. Will try to retry over Http."; private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class); - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss:SSSS"); private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"}; private static final String HTTPS_SCHEMA = "https://"; private static final String HTTP_SCHEMA = "http://"; + private final Logging loggingService; + private final EELFLogger outgoingRequestsLogger; private RestClient restClient; - public SyncRestClient() { - restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(defaultHttpClient()).build(); + public SyncRestClient(Logging loggingService) { + this(null, null, loggingService, false); } - public SyncRestClient(ObjectMapper objectMapper) { - restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(defaultHttpClient()).build(); + public SyncRestClient(Logging loggingService, boolean useLoggingInterceptor) { + this(null, null, loggingService, useLoggingInterceptor); } - public SyncRestClient(CloseableHttpClient httpClient) { - restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(httpClient).build(); + public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) { + this(null, objectMapper, loggingService, true); } - public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper) { - restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(httpClient).build(); + public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) { + this(httpClient, null, loggingService, false); + } + + public SyncRestClient(CloseableHttpClient httpClient, + ObjectMapper objectMapper, + Logging loggingService, + boolean useLoggingInterceptor) { + restClient = RestClient + .newClient() + .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper())) + .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient(useLoggingInterceptor))) + .build(); + this.loggingService = loggingService; + this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient"); } @Override public HttpResponse post(String url, Map headers, Object body) { - return callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson()); + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body); + + HttpResponse response = callWithRetryOverHttp(url, + url2 -> restClient.post(url2).headers(headers).body(body).asJson()); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response); + + return response; } @Override public HttpResponse post(String url, Map headers, Object body, Class responseClass) { - return callWithRetryOverHttp(url, + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body); + + HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass)); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response); + + return response; } @Override public HttpResponse get(String url, Map headers, Map routeParams) { - return callWithRetryOverHttp(url, url2 -> { - val getRequest = restClient.get(url2).headers(headers); + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); + + HttpResponse response = callWithRetryOverHttp(url, url2 -> { + GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asJson(); }); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response); + + return response; } @Override public HttpResponse get(String url, Map headers, Map routeParams, - Class responseClass) { - return callWithRetryOverHttp(url, url2 -> { - val getRequest = restClient.get(url2).headers(headers); + Class responseClass) { + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); + + HttpResponse response = callWithRetryOverHttp(url, url2 -> { + GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asObject(responseClass); }); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response); + + return response; } @Override public HttpResponse getStream(String url, Map headers, - Map routeParams) { - return callWithRetryOverHttp(url, url2 -> { - val getRequest = restClient.get(url2).headers(headers); + Map routeParams) { + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); + + HttpResponse response = callWithRetryOverHttp(url, url2 -> { + GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asBinary(); }); + + //no logging of the response since the response is too long + return response; + } @Override public HttpResponse put(String url, Map headers, Object body) { - return callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson()); + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body); + + HttpResponse response = callWithRetryOverHttp(url, + url2 -> restClient.put(url2).headers(headers).body(body).asJson()); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response); + + return response; } @Override public HttpResponse put(String url, Map headers, Object body, Class responseClass) { - return callWithRetryOverHttp(url, + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body); + + HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass)); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response); + + return response; + } + + @Override + public HttpResponse delete(String url, Map headers, Object body, Class responseClass) { + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url, body); + + HttpResponse response = callWithRetryOverHttp(url, + url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass)); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); + + return response; } @Override public HttpResponse delete(String url, Map headers, Class responseClass) { - return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass)); + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url); + + HttpResponse response = callWithRetryOverHttp(url, + url2 -> restClient.delete(url2).headers(headers).asObject(responseClass)); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); + + return response; } @Override public HttpResponse delete(String url, Map headers) { - return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson()); + loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url); + + HttpResponse response = callWithRetryOverHttp(url, + url2 -> restClient.delete(url2).headers(headers).asJson()); + + loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); + + return response; } @Override @@ -149,81 +235,86 @@ public class SyncRestClient implements SyncRestClientInterface { restClient.shutdown(); } - @SneakyThrows - private HttpResponse callWithRetryOverHttp(String url, - CheckedFunction1> httpRequest) { + private HttpResponse callWithRetryOverHttp(String url, HttpRequest httpRequest) { + try { + return callWithRetryOverHttpThrows(url, httpRequest); + } catch (IOException e) { + throw new SyncRestClientException("IOException while calling rest service", e); + } + } + + private HttpResponse callWithRetryOverHttpThrows(String url, HttpRequest httpRequest) throws IOException { try { - return httpRequest.apply(url); + return patched(httpRequest.apply(url)); } catch (RestClientException e) { - if (e.getCause() instanceof SSLException) { - logger.warn(EELFLoggerDelegate.debugLogger, - DATE_FORMAT.format(new Date()) + TRY_TO_CALL_OVER_HTTP, e); - return httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)); + if (causedBySslHandshakeError(e)) { + logger.warn("SSL Handshake problem occured. Will try to retry over Http.", e); + return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA))); } throw e; } } - private ObjectMapper defaultObjectMapper() { - val objectMapper = new org.codehaus.jackson.map.ObjectMapper(); - - return new ObjectMapper() { - @Override - @SneakyThrows - public T readValue(String value, Class aClass) { - return objectMapper.readValue(value, aClass); - } + private boolean causedBySslHandshakeError(RestClientException exception) { + return exception.getCause() instanceof SSLException; + } - @Override - @SneakyThrows - public String writeValue(Object value) { - return objectMapper.writeValueAsString(value); - } - }; + private ObjectMapper defaultObjectMapper() { + return JOSHWORKS_JACKSON_OBJECT_MAPPER; } - private CloseableHttpClient defaultHttpClient() { + private CloseableHttpClient defaultHttpClient(boolean useLoggingInterceptor) { try { - val trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME); - val trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X); - val decryptedTrustStorePass = Password.deobfuscate(trustStorePass); + String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME); + String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X); + String decryptedTrustStorePass = Password.deobfuscate(trustStorePass); - val trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass); - val sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore); - val sslSf = allowTLSProtocols(sslContext); + KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass); + SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore); + SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext); - return HttpClients.custom().setSSLSocketFactory(sslSf).build(); + HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSf); + if (useLoggingInterceptor) { + httpClientBuilder + .addInterceptorFirst(new ApacheClientMetricRequestInterceptor()) + .addInterceptorLast(new ApacheClientMetricResponseInterceptor()); + } + return httpClientBuilder.build(); } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { - logger.warn(EELFLoggerDelegate.debugLogger, - DATE_FORMAT.format(new Date()) + CANNOT_INITIALIZE_CUSTOM_HTTP_CLIENT, e); + logger.warn("Cannot initialize custom http client from current configuration. Using default one.", e); return HttpClients.createDefault(); } } private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) { return new SSLConnectionSocketFactory( - sslcontext, - SUPPORTED_SSL_VERSIONS, - null, - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + sslcontext, + SUPPORTED_SSL_VERSIONS, + null, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { return SSLContexts.custom() - .useTLS() - .loadKeyMaterial(trustStore, trustStorePass.toCharArray()) - .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) - .build(); + .useTLS() + .loadKeyMaterial(trustStore, trustStorePass.toCharArray()) + .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) + .build(); } private KeyStore loadTruststore(String trustStorePath, String trustStorePass) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) { trustStore.load(instream, trustStorePass.toCharArray()); } return trustStore; } + @FunctionalInterface + private interface HttpRequest { + HttpResponse apply(String url) throws IOException; + } + }