Generic REST client for external services 43/58743/18
authorgolabek <tomasz.golabek@nokia.com>
Wed, 1 Aug 2018 13:56:44 +0000 (15:56 +0200)
committerWojciech Sliwka <wojciech.sliwka@nokia.com>
Thu, 23 Aug 2018 09:02:34 +0000 (11:02 +0200)
Initial commit for generic REST client for requests to another components APIs

Change-Id: I4af29ab1e45fd805403622e60585a351f3ef5d20
Issue-ID: VID-266
Signed-off-by: tgolabek <tomasz.golabek@nokia.com>
vid-app-common/pom.xml
vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java [new file with mode: 0644]
vid-app-common/src/main/java/org/onap/vid/client/SyncRestClientInterface.java [new file with mode: 0644]
vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpServerTest.java [new file with mode: 0644]
vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpsServerTest.java [new file with mode: 0644]
vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientModel.java [new file with mode: 0644]

index b8fbd85..9a6210b 100755 (executable)
                    <artifactId>opencsv</artifactId>\r
                    <version>4.1</version>\r
                </dependency>\r
-\r
+                       \r
                <!-- HTTP client -->\r
+\r
+               <dependency>\r
+                       <groupId>io.joshworks.unirest</groupId>\r
+                       <artifactId>unirest-java</artifactId>\r
+                       <version>0.2.1</version>\r
+               </dependency>\r
+               <dependency>\r
+                       <groupId>org.apache.httpcomponents</groupId>\r
+                       <artifactId>httpclient</artifactId>\r
+                       <version>4.3.6</version>\r
+               </dependency>\r
+               <dependency>\r
+                       <groupId>org.apache.httpcomponents</groupId>\r
+                       <artifactId>httpasyncclient</artifactId>\r
+                       <version>4.0.2</version>\r
+               </dependency>\r
+               <dependency>\r
+                       <groupId>org.apache.httpcomponents</groupId>\r
+                       <artifactId>httpmime</artifactId>\r
+                       <version>4.3.6</version>\r
+               </dependency>\r
                <dependency>\r
                        <groupId>com.xebialabs.restito</groupId>\r
                        <artifactId>restito</artifactId>\r
                        <version>0.9.3</version>\r
                        <scope>test</scope>\r
                </dependency>\r
-                       \r
-               <!-- SDK overlay war -->\r
 \r
+               <!-- Helpers -->\r
+               <dependency>\r
+                       <groupId>org.projectlombok</groupId>\r
+                       <artifactId>lombok</artifactId>\r
+                       <version>1.18.2</version>\r
+                       <scope>provided</scope>\r
+               </dependency>\r
+               <dependency>\r
+                       <groupId>io.vavr</groupId>\r
+                       <artifactId>vavr</artifactId>\r
+                       <version>0.9.2</version>\r
+               </dependency>\r
+\r
+               <!-- SDK overlay war -->\r
                <dependency>\r
                        <groupId>org.onap.portal.sdk</groupId>\r
                        <artifactId>epsdk-app-overlay</artifactId>\r
                        <artifactId>junit</artifactId>\r
                        <version>4.12</version>\r
                </dependency>\r
+               <dependency>\r
+                       <groupId>commons-io</groupId>\r
+                       <artifactId>commons-io</artifactId>\r
+                       <version>2.4</version>\r
+                       <!--<scope>test</scope>-->\r
+               </dependency>\r
                <dependency>\r
                        <groupId>com.google.code.bean-matchers</groupId>\r
                        <artifactId>bean-matchers</artifactId>\r
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
new file mode 100644 (file)
index 0000000..84e83e8
--- /dev/null
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.vid.client;
+
+import io.joshworks.restclient.http.HttpResponse;
+import io.joshworks.restclient.http.JsonNode;
+import io.joshworks.restclient.http.RestClient;
+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.SSLHandshakeException;
+import java.security.KeyStoreException;
+import java.text.SimpleDateFormat;
+import javax.net.ssl.SSLContext;
+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.util.Map;
+import java.io.File;
+import org.onap.portalsdk.core.util.SystemProperties;
+import org.onap.vid.properties.VidProperties;
+
+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 RestClient restClient;
+
+    public SyncRestClient() {
+        restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(defaultHttpClient()).build();
+    }
+
+    public SyncRestClient(ObjectMapper objectMapper) {
+        restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(defaultHttpClient()).build();
+    }
+
+    public SyncRestClient(CloseableHttpClient httpClient) {
+        restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(httpClient).build();
+    }
+
+    public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper) {
+        restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(httpClient).build();
+    }
+
+    @Override
+    public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
+        return callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson());
+    }
+
+    @Override
+    public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
+        return callWithRetryOverHttp(url,
+            url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
+    }
+
+    @Override
+    public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
+        return callWithRetryOverHttp(url, url2 -> {
+            val getRequest = restClient.get(url2).headers(headers);
+            routeParams.forEach(getRequest::routeParam);
+            return getRequest.asJson();
+        });
+    }
+
+    @Override
+    public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
+        Class<T> responseClass) {
+        return callWithRetryOverHttp(url, url2 -> {
+            val getRequest = restClient.get(url2).headers(headers);
+            routeParams.forEach(getRequest::routeParam);
+            return getRequest.asObject(responseClass);
+        });
+    }
+
+    @Override
+    public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
+        Map<String, String> routeParams) {
+        return callWithRetryOverHttp(url, url2 -> {
+            val getRequest = restClient.get(url2).headers(headers);
+            routeParams.forEach(getRequest::routeParam);
+            return getRequest.asBinary();
+        });
+    }
+
+    @Override
+    public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
+        return callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson());
+    }
+
+    @Override
+    public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
+        return callWithRetryOverHttp(url,
+            url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
+    }
+
+    @Override
+    public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
+        return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
+    }
+
+    @Override
+    public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
+        return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson());
+    }
+
+    @Override
+    public void destroy() {
+        restClient.shutdown();
+    }
+
+    @SneakyThrows
+    private <T> HttpResponse<T> callWithRetryOverHttp(String url,
+        CheckedFunction1<String, HttpResponse<T>> httpRequest) {
+        try {
+            return httpRequest.apply(url);
+        } catch (Exception e) {
+            if (e.getCause() instanceof SSLHandshakeException) {
+                logger.warn(EELFLoggerDelegate.debugLogger,
+                    DATE_FORMAT.format(new Date()) + TRY_TO_CALL_OVER_HTTP, e);
+                return 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> T readValue(String value, Class<T> aClass) {
+                return objectMapper.readValue(value, aClass);
+            }
+
+            @Override
+            @SneakyThrows
+            public String writeValue(Object value) {
+                return objectMapper.writeValueAsString(value);
+            }
+        };
+    }
+
+    private CloseableHttpClient defaultHttpClient() {
+        try {
+            val trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
+            val trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
+            val decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
+
+            val trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
+            val sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
+            val sslSf = allowTLSProtocols(sslContext);
+
+            return HttpClients.custom().setSSLSocketFactory(sslSf).build();
+        } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
+            logger.warn(EELFLoggerDelegate.debugLogger,
+                DATE_FORMAT.format(new Date()) + CANNOT_INITIALIZE_CUSTOM_HTTP_CLIENT, e);
+            return HttpClients.createDefault();
+        }
+    }
+
+    private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
+        return new SSLConnectionSocketFactory(
+            sslcontext,
+            SUPPORTED_SSL_VERSIONS,
+            null,
+            SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+    }
+
+    private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
+        throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        return SSLContexts.custom()
+            .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());
+        try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
+            trustStore.load(instream, trustStorePass.toCharArray());
+        }
+        return trustStore;
+    }
+
+}
diff --git a/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClientInterface.java b/vid-app-common/src/main/java/org/onap/vid/client/SyncRestClientInterface.java
new file mode 100644 (file)
index 0000000..80663d6
--- /dev/null
@@ -0,0 +1,30 @@
+package org.onap.vid.client;
+
+import io.joshworks.restclient.http.HttpResponse;
+import io.joshworks.restclient.http.JsonNode;
+import java.io.InputStream;
+import java.util.Map;
+
+public interface SyncRestClientInterface {
+
+    HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body);
+
+    <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> aClass);
+
+    HttpResponse<JsonNode> get(String url, Map<String, String> headers,  Map<String, String> routeParams);
+
+    <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams, Class<T> aClass);
+
+    HttpResponse<InputStream> getStream(String url, Map<String, String> headers, Map<String, String> routeParams);
+
+    HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body);
+
+    <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body,  Class<T> aClass);
+
+    <T> HttpResponse<T> delete(String url, Map<String, String> headers,  Class<T> aClass);
+
+    HttpResponse<JsonNode> delete(String url, Map<String, String> headers);
+
+    void destroy();
+
+}
diff --git a/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpServerTest.java b/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpServerTest.java
new file mode 100644 (file)
index 0000000..d63d144
--- /dev/null
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.vid.client;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import com.xebialabs.restito.semantics.Condition;
+import com.xebialabs.restito.server.StubServer;
+import io.joshworks.restclient.http.HttpResponse;
+import io.joshworks.restclient.http.JsonNode;
+import org.glassfish.grizzly.http.util.HttpStatus;
+import com.xebialabs.restito.semantics.Action;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
+import org.glassfish.grizzly.http.Method;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static com.xebialabs.restito.builder.verify.VerifyHttp.verifyHttp;
+import static com.xebialabs.restito.builder.stub.StubHttp.whenHttp;
+import static com.xebialabs.restito.semantics.Action.stringContent;
+import static com.xebialabs.restito.semantics.Action.contentType;
+import static com.xebialabs.restito.semantics.Action.status;
+import static com.xebialabs.restito.semantics.Action.ok;
+import static org.testng.Assert.assertEquals;
+
+public class SyncRestClientForHttpServerTest {
+
+    private static final SyncRestClientModel.TestModel testObject = new SyncRestClientModel.TestModel(1, "test");
+    private static final String NOT_EXISTING_OBJECT = "NOT EXISTING OBJECT";
+
+    private StubServer stubServer;
+    private ObjectMapper objectMapper = new ObjectMapper();
+    private SyncRestClient syncRestClient;
+
+    @BeforeMethod
+    public void setUp() {
+        stubServer = new StubServer();
+        stubServer.run();
+        syncRestClient = new SyncRestClient();
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        stubServer.stop();
+        syncRestClient.destroy();
+    }
+
+    @Test
+    public void testJsonResponseFromGet() throws JsonProcessingException {
+        // given
+        stubGetCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient
+            .get(url, Collections.emptyMap(), Collections.emptyMap());
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.GET), Condition.url(url));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("key"), 1);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("value"), "test");
+    }
+
+    @Test
+    public void testObjectResponseFromGet() throws JsonProcessingException {
+        // given
+        stubGetCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<SyncRestClientModel.TestModel> testModelHttpResponse = syncRestClient
+            .get(url, Collections.emptyMap(), Collections.emptyMap(), SyncRestClientModel.TestModel.class);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.GET), Condition.url(url));
+        assertEquals(testModelHttpResponse.getStatus(), 200);
+        assertEquals(testModelHttpResponse.getBody().getKey(), 1);
+        assertEquals(testModelHttpResponse.getBody().getValue(), "test");
+    }
+
+    @Test
+    public void testJsonResponseFromPost() throws JsonProcessingException {
+        // given
+        stubPostCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient.post(url, Collections.emptyMap(), testObject);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.POST), Condition.url(url));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("key"), 1);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("value"), "test");
+    }
+
+    @Test
+    public void test404JsonResponseFromPost() throws JsonProcessingException {
+        // given
+        stubPostCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient
+            .post(url, Collections.emptyMap(), NOT_EXISTING_OBJECT);
+        // then
+        assertEquals(jsonNodeHttpResponse.getStatus(), 404);
+        assertEquals(jsonNodeHttpResponse.getStatusText(), "Not Found");
+    }
+
+    @Test
+    public void testHeadersWerePassedToPost() throws JsonProcessingException {
+        // given
+        stubPostCall();
+        Map headers = ImmutableMap.<String, String>builder().put("Authorization", "Basic anyHash").build();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient.post(url, headers, testObject);
+        // then
+        verifyHttp(stubServer).once(Condition.withHeader("Authorization"));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+    }
+
+    @Test(expectedExceptions = {Exception.class})
+    public void testFailedJsonResponseFromPost() throws JsonProcessingException {
+        // given
+        stubPostCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        stubServer.stop();
+        syncRestClient.post(url, Collections.emptyMap(), testObject);
+    }
+
+    @Test
+    public void testObjectResponseFromPost() throws JsonProcessingException {
+        // given
+        stubPostCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<SyncRestClientModel.TestModel> objectHttpResponse = syncRestClient
+            .post(url, Collections.emptyMap(), testObject, SyncRestClientModel.TestModel.class);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.POST), Condition.url(url));
+        assertEquals(objectHttpResponse.getStatus(), 200);
+        assertEquals(objectHttpResponse.getBody().getKey(), 1);
+        assertEquals(objectHttpResponse.getBody().getValue(), "test");
+    }
+
+    @Test
+    public void testJsonResponseFromPut() throws JsonProcessingException {
+        // given
+        stubPutCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient.put(url, Collections.emptyMap(), testObject);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.PUT), Condition.url(url));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 201);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("key"), 1);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("value"), "test");
+    }
+
+    @Test
+    public void testObjectResponseFromPut() throws JsonProcessingException {
+        // given
+        stubPutCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<SyncRestClientModel.TestModel> modelHttpResponse = syncRestClient
+            .put(url, Collections.emptyMap(), testObject, SyncRestClientModel.TestModel.class);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.PUT), Condition.url(url));
+        assertEquals(modelHttpResponse.getStatus(), 201);
+        assertEquals(modelHttpResponse.getBody().getKey(), 1);
+        assertEquals(modelHttpResponse.getBody().getValue(), "test");
+    }
+
+    @Test
+    public void testJsonResponseFromDelete() throws JsonProcessingException {
+        // given
+        stubDeleteCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient.delete(url, Collections.emptyMap());
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.DELETE), Condition.url(url));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("key"), 1);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("value"), "test");
+    }
+
+    @Test
+    public void testObjectResponseFromDelete() throws JsonProcessingException {
+        // given
+        stubDeleteCall();
+        String url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<SyncRestClientModel.TestModel> modelHttpResponse = syncRestClient
+            .delete(url, Collections.emptyMap(),  SyncRestClientModel.TestModel.class);
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.DELETE), Condition.url(url));
+        assertEquals(modelHttpResponse.getStatus(), 200);
+        assertEquals(modelHttpResponse.getBody().getKey(), 1);
+        assertEquals(modelHttpResponse.getBody().getValue(), "test");
+    }
+
+    @Test
+    public void testRedirectToHttp() throws JsonProcessingException {
+        // given
+        stubGetCall();
+        String secured_url = "https://0.0.0.0:" + stubServer.getPort() + "/test";
+        String available_url = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient
+            .get(secured_url, Collections.emptyMap(), Collections.emptyMap());
+        // then
+        verifyHttp(stubServer).once(Condition.method(Method.GET), Condition.url(available_url),
+            Condition.not(Condition.url(secured_url)));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+    }
+
+    private void stubGetCall() throws JsonProcessingException {
+        whenHttp(stubServer)
+            .match(Condition.get("/test"))
+            .then(ok(), jsonContent(), contentType("application/json"));
+    }
+
+    private void stubDeleteCall() throws JsonProcessingException {
+        whenHttp(stubServer)
+            .match(Condition.delete("/test"))
+            .then(ok(), jsonContent(), contentType("application/json"));
+    }
+
+    private void stubPostCall() throws JsonProcessingException {
+        whenHttp(stubServer)
+            .match(Condition.post("/test"),
+                Condition.withPostBodyContaining(objectMapper.writeValueAsString(testObject)))
+            .then(ok(), jsonContent(), contentType("application/json"));
+    }
+
+    private void stubPutCall() throws JsonProcessingException {
+        whenHttp(stubServer)
+            .match(Condition.put("/test"),
+                Condition.withPostBodyContaining(objectMapper.writeValueAsString(testObject)))
+            .then(status(HttpStatus.CREATED_201), jsonContent(), contentType("application/json"));
+    }
+
+    private Action jsonContent() throws JsonProcessingException {
+        return stringContent(objectMapper.writeValueAsString(testObject));
+    }
+
+}
\ No newline at end of file
diff --git a/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpsServerTest.java b/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientForHttpsServerTest.java
new file mode 100644 (file)
index 0000000..ba6da43
--- /dev/null
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.vid.client;
+
+import io.joshworks.restclient.http.HttpResponse;
+import io.joshworks.restclient.http.JsonNode;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import com.xebialabs.restito.semantics.Condition;
+import com.xebialabs.restito.server.StubServer;
+import com.xebialabs.restito.semantics.Action;
+import org.apache.http.config.RegistryBuilder;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
+import org.glassfish.grizzly.http.Method;
+import org.apache.http.client.HttpClient;
+import org.apache.http.config.Registry;
+import org.testng.annotations.Test;
+
+import java.security.GeneralSecurityException;
+import javax.net.ssl.SSLContext;
+import java.util.Collections;
+
+import static com.xebialabs.restito.builder.verify.VerifyHttp.verifyHttp;
+import static com.xebialabs.restito.builder.stub.StubHttp.whenHttp;
+import static com.xebialabs.restito.semantics.Action.stringContent;
+import static com.xebialabs.restito.semantics.Action.contentType;
+import static org.apache.http.client.config.RequestConfig.custom;
+import static com.xebialabs.restito.semantics.Action.ok;
+import static org.testng.Assert.assertEquals;
+
+public class SyncRestClientForHttpsServerTest {
+
+    private static final SyncRestClientModel.TestModel testObject = new SyncRestClientModel.TestModel(1, "test");
+    private static final String[] SUPPORTED_PROTOCOLS = {"TLSv1", "TLSv1.2"};
+    private StubServer stubServer;
+    private ObjectMapper objectMapper = new ObjectMapper();
+
+    private SyncRestClient syncRestClient;
+
+    @BeforeMethod
+    public void setUp() throws GeneralSecurityException {
+        stubServer = new StubServer();
+        stubServer.secured().run();
+        syncRestClient = new SyncRestClient(createNaiveHttpClient());
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        stubServer.stop();
+    }
+
+    @Test
+    public void testJsonResponseFromGet() throws JsonProcessingException {
+        // given
+        stubGetCall();
+        String securedUrl = "https://0.0.0.0:" + stubServer.getPort() + "/test";
+        String notSecuredUrl = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<JsonNode> jsonNodeHttpResponse = syncRestClient
+            .get(securedUrl, Collections.emptyMap(), Collections.emptyMap());
+        // then
+        verifyHttp(stubServer)
+            .once(Condition.method(Method.GET), Condition.url(securedUrl), Condition.not(Condition.url(notSecuredUrl)));
+        assertEquals(jsonNodeHttpResponse.getStatus(), 200);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("key"), 1);
+        assertEquals(jsonNodeHttpResponse.getBody().getObject().get("value"), "test");
+    }
+
+    @Test
+    public void testObjectResponseFromGet() throws JsonProcessingException {
+        // given
+        stubServer.run();
+        stubGetCall();
+        String securedUrl = "https://0.0.0.0:" + stubServer.getPort() + "/test";
+        String notSecuredUrl = "http://0.0.0.0:" + stubServer.getPort() + "/test";
+        // when
+        HttpResponse<SyncRestClientModel.TestModel> testModelHttpResponse = syncRestClient
+            .get(securedUrl, Collections.emptyMap(), Collections.emptyMap(), SyncRestClientModel.TestModel.class);
+        // then
+        verifyHttp(stubServer)
+            .once(Condition.method(Method.GET), Condition.url(securedUrl), Condition.not(Condition.url(notSecuredUrl)));
+        assertEquals(testModelHttpResponse.getStatus(), 200);
+        assertEquals(testModelHttpResponse.getBody().getKey(), 1);
+        assertEquals(testModelHttpResponse.getBody().getValue(), "test");
+    }
+
+    private Action jsonContent() throws JsonProcessingException {
+        return stringContent(objectMapper.writeValueAsString(testObject));
+    }
+
+    private void stubGetCall() throws JsonProcessingException {
+        whenHttp(stubServer)
+            .match(Condition.get("/test"))
+            .then(ok(), jsonContent(), contentType("application/json"));
+    }
+
+    private CloseableHttpClient createNaiveHttpClient() throws GeneralSecurityException {
+        final SSLContext context = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy())
+            .build();
+
+        final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(context, SUPPORTED_PROTOCOLS,
+            null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
+            .register("https", socketFactory)
+            .build();
+
+        return HttpClientBuilder.create()
+            .setDefaultRequestConfig(custom().setConnectionRequestTimeout(10000).build())
+            .setConnectionManager(new PoolingHttpClientConnectionManager(registry))
+            .setSSLSocketFactory(socketFactory).build();
+    }
+
+}
\ No newline at end of file
diff --git a/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientModel.java b/vid-app-common/src/test/java/org/onap/vid/client/SyncRestClientModel.java
new file mode 100644 (file)
index 0000000..b64988a
--- /dev/null
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.vid.client;
+
+class SyncRestClientModel {
+
+    static class TestModel{
+
+        public TestModel() {
+            // needed by the object mappers
+        }
+
+        public TestModel(long key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        private long key;
+        private String value;
+
+        public void setKey(long key) {
+            this.key = key;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        public long getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}