create JoshworksJacksonObjectMapper and use it everywhere needed
[vid.git] / vid-app-common / src / main / java / org / onap / vid / client / SyncRestClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2018 - 2019 Nokia. All rights reserved.
6  * Modifications Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
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
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
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=========================================================
20  */
21
22 package org.onap.vid.client;
23
24 import static org.onap.vid.client.UnirestPatchKt.patched;
25 import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import io.joshworks.restclient.http.HttpResponse;
29 import io.joshworks.restclient.http.JsonNode;
30 import io.joshworks.restclient.http.RestClient;
31 import io.joshworks.restclient.http.exceptions.RestClientException;
32 import io.joshworks.restclient.http.mapper.ObjectMapper;
33 import io.joshworks.restclient.request.GetRequest;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.security.KeyManagementException;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.UnrecoverableKeyException;
43 import java.security.cert.CertificateException;
44 import java.util.Map;
45 import javax.net.ssl.SSLContext;
46 import javax.net.ssl.SSLException;
47 import org.apache.commons.lang3.ObjectUtils;
48 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
49 import org.apache.http.conn.ssl.SSLContexts;
50 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
51 import org.apache.http.impl.client.CloseableHttpClient;
52 import org.apache.http.impl.client.HttpClients;
53 import org.eclipse.jetty.util.security.Password;
54 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
55 import org.onap.portalsdk.core.util.SystemProperties;
56 import org.onap.vid.properties.VidProperties;
57 import org.onap.vid.utils.Logging;
58
59 public class SyncRestClient implements SyncRestClientInterface {
60     private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
61     private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
62     private static final String HTTPS_SCHEMA = "https://";
63     private static final String HTTP_SCHEMA = "http://";
64     private final Logging loggingService;
65     private final EELFLogger outgoingRequestsLogger;
66
67     private RestClient restClient;
68
69     public SyncRestClient(Logging loggingService) {
70         this(null, null, loggingService);
71     }
72
73     public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) {
74         this(null, objectMapper,  loggingService);
75     }
76
77     public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) {
78         this(httpClient, null,  loggingService);
79     }
80
81     public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper, Logging loggingService) {
82         restClient = RestClient
83             .newClient()
84             .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper()))
85             .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient()))
86             .build();
87         this.loggingService = loggingService;
88         this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient");
89     }
90
91     @Override
92     public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
93         return callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson());
94     }
95
96     @Override
97     public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
98         return callWithRetryOverHttp(url,
99                 url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
100     }
101
102     @Override
103     public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
104         return callWithRetryOverHttp(url, url2 -> {
105             GetRequest getRequest = restClient.get(url2).headers(headers);
106             routeParams.forEach(getRequest::routeParam);
107             return getRequest.asJson();
108         });
109     }
110
111     @Override
112     public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
113                                    Class<T> responseClass) {
114         return callWithRetryOverHttp(url, url2 -> {
115             GetRequest getRequest = restClient.get(url2).headers(headers);
116             routeParams.forEach(getRequest::routeParam);
117             return getRequest.asObject(responseClass);
118         });
119     }
120
121     @Override
122     public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
123                                                Map<String, String> routeParams) {
124         return callWithRetryOverHttp(url, url2 -> {
125             GetRequest getRequest = restClient.get(url2).headers(headers);
126             routeParams.forEach(getRequest::routeParam);
127             return getRequest.asBinary();
128         });
129     }
130
131     @Override
132     public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
133         return callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson());
134     }
135
136     @Override
137     public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
138         return callWithRetryOverHttp(url,
139                 url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
140     }
141
142     @Override
143     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
144         return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass));
145     }
146
147     @Override
148     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
149         return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
150     }
151
152     @Override
153     public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
154         return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson());
155     }
156
157     @Override
158     public void destroy() {
159         restClient.shutdown();
160     }
161
162     private <T> HttpResponse<T> callWithRetryOverHttp(String url, HttpRequest<T> httpRequest) {
163         try {
164             return callWithRetryOverHttpThrows(url, httpRequest);
165         } catch (IOException e) {
166             throw new SyncRestClientException("IOException while calling rest service", e);
167         }
168     }
169
170     private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
171         try {
172             return patched(httpRequest.apply(url));
173         } catch (RestClientException e) {
174             if (causedBySslHandshakeError(e)) {
175                 logger.warn(EELFLoggerDelegate.debugLogger, "SSL Handshake problem occured. Will try to retry over Http.", e);
176                 return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)));
177             }
178             throw e;
179         }
180     }
181
182     private boolean causedBySslHandshakeError(RestClientException exception) {
183         return exception.getCause() instanceof SSLException;
184     }
185
186     private ObjectMapper defaultObjectMapper() {
187         return JOSHWORKS_JACKSON_OBJECT_MAPPER;
188     }
189
190     private CloseableHttpClient defaultHttpClient() {
191         try {
192             String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
193             String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
194             String decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
195
196             KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
197             SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
198             SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext);
199
200             return HttpClients.custom().setSSLSocketFactory(sslSf).build();
201         } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
202             logger.warn(EELFLoggerDelegate.debugLogger, "Cannot initialize custom http client from current configuration. Using default one.", e);
203             return HttpClients.createDefault();
204         }
205     }
206
207     private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
208         return new SSLConnectionSocketFactory(
209                 sslcontext,
210                 SUPPORTED_SSL_VERSIONS,
211                 null,
212                 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
213     }
214
215     private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
216             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
217         return SSLContexts.custom()
218                 .useTLS()
219                 .loadKeyMaterial(trustStore, trustStorePass.toCharArray())
220                 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
221                 .build();
222     }
223
224     private KeyStore loadTruststore(String trustStorePath, String trustStorePass)
225             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
226         KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
227         try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
228             trustStore.load(instream, trustStorePass.toCharArray());
229         }
230         return trustStore;
231     }
232
233     @FunctionalInterface
234     private interface HttpRequest<T> {
235         HttpResponse<T> apply(String url) throws IOException;
236     }
237
238 }