2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Nokia. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.vid.client;
23 import io.joshworks.restclient.http.HttpResponse;
24 import io.joshworks.restclient.http.JsonNode;
25 import io.joshworks.restclient.http.RestClient;
26 import io.joshworks.restclient.http.exceptions.RestClientException;
27 import io.joshworks.restclient.http.mapper.ObjectMapper;
28 import io.joshworks.restclient.request.GetRequest;
29 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
30 import org.apache.http.conn.ssl.SSLContexts;
31 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
32 import org.apache.http.impl.client.CloseableHttpClient;
33 import org.apache.http.impl.client.HttpClients;
34 import org.eclipse.jetty.util.security.Password;
35 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
36 import org.onap.portalsdk.core.util.SystemProperties;
37 import org.onap.vid.properties.VidProperties;
39 import javax.net.ssl.SSLContext;
40 import javax.net.ssl.SSLException;
42 import java.io.FileInputStream;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.security.*;
46 import java.security.cert.CertificateException;
49 public class SyncRestClient implements SyncRestClientInterface {
50 private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
51 private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
52 private static final String HTTPS_SCHEMA = "https://";
53 private static final String HTTP_SCHEMA = "http://";
55 private RestClient restClient;
57 public SyncRestClient() {
58 restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(defaultHttpClient()).build();
61 public SyncRestClient(ObjectMapper objectMapper) {
62 restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(defaultHttpClient()).build();
65 public SyncRestClient(CloseableHttpClient httpClient) {
66 restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(httpClient).build();
69 public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper) {
70 restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(httpClient).build();
74 public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
75 return callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson());
79 public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
80 return callWithRetryOverHttp(url,
81 url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
85 public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
86 return callWithRetryOverHttp(url, url2 -> {
87 GetRequest getRequest = restClient.get(url2).headers(headers);
88 routeParams.forEach(getRequest::routeParam);
89 return getRequest.asJson();
94 public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
95 Class<T> responseClass) {
96 return callWithRetryOverHttp(url, url2 -> {
97 GetRequest getRequest = restClient.get(url2).headers(headers);
98 routeParams.forEach(getRequest::routeParam);
99 return getRequest.asObject(responseClass);
104 public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
105 Map<String, String> routeParams) {
106 return callWithRetryOverHttp(url, url2 -> {
107 GetRequest getRequest = restClient.get(url2).headers(headers);
108 routeParams.forEach(getRequest::routeParam);
109 return getRequest.asBinary();
114 public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
115 return callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson());
119 public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
120 return callWithRetryOverHttp(url,
121 url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
125 public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
126 return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass));
130 public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
131 return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
135 public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
136 return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson());
140 public void destroy() {
141 restClient.shutdown();
144 private <T> HttpResponse<T> callWithRetryOverHttp(String url, HttpRequest<T> httpRequest) {
146 return callWithRetryOverHttpThrows(url, httpRequest);
147 } catch (IOException e) {
148 throw new SyncRestClientException("IOException while calling rest service", e);
152 private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
154 return httpRequest.apply(url);
155 } catch (RestClientException e) {
156 if (causedBySslHandshakeError(e)) {
157 logger.warn(EELFLoggerDelegate.debugLogger, "SSL Handshake problem occured. Will try to retry over Http.", e);
158 return httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA));
164 private boolean causedBySslHandshakeError(RestClientException exception) {
165 return exception.getCause() instanceof SSLException;
168 private ObjectMapper defaultObjectMapper() {
169 com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
171 return new ObjectMapper() {
173 public <T> T readValue(String value, Class<T> aClass) {
175 return objectMapper.readValue(value, aClass);
176 } catch (IOException e) {
177 throw new SyncRestClientException("IOException while reading value", e);
182 public String writeValue(Object value) {
184 return objectMapper.writeValueAsString(value);
185 } catch (IOException e) {
186 throw new SyncRestClientException("IOException while writing value", e);
192 private CloseableHttpClient defaultHttpClient() {
194 String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
195 String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
196 String decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
198 KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
199 SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
200 SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext);
202 return HttpClients.custom().setSSLSocketFactory(sslSf).build();
203 } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
204 logger.warn(EELFLoggerDelegate.debugLogger, "Cannot initialize custom http client from current configuration. Using default one.", e);
205 return HttpClients.createDefault();
209 private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
210 return new SSLConnectionSocketFactory(
212 SUPPORTED_SSL_VERSIONS,
214 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
217 private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
218 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
219 return SSLContexts.custom()
221 .loadKeyMaterial(trustStore, trustStorePass.toCharArray())
222 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
226 private KeyStore loadTruststore(String trustStorePath, String trustStorePass)
227 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
228 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
229 try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
230 trustStore.load(instream, trustStorePass.toCharArray());
236 private interface HttpRequest<T> {
237 HttpResponse<T> apply(String url) throws IOException;