Patch NPE in Unirest HttpResponse::getBody when getRawBody is null 87/91987/5
authorIttay Stern <ittay.stern@att.com>
Thu, 25 Jul 2019 17:33:03 +0000 (20:33 +0300)
committerIttay Stern <ittay.stern@att.com>
Mon, 29 Jul 2019 06:57:54 +0000 (09:57 +0300)
Issue-ID: VID-267

Change-Id: I469bbeea52d6e86f8f12f88e9754af81b3ae6ae6
Signed-off-by: Ittay Stern <ittay.stern@att.com>
vid-app-common/src/main/java/org/onap/vid/client/SyncRestClient.java
vid-app-common/src/main/java/org/onap/vid/client/UnirestPatch.kt [new file with mode: 0644]
vid-app-common/src/main/java/org/onap/vid/controller/WebConfig.java

index 5f76044..50556e7 100644 (file)
 
 package org.onap.vid.client;
 
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+import static org.onap.vid.client.UnirestPatchKt.patched;
+
 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 io.joshworks.restclient.request.GetRequest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+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 javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 import org.apache.http.conn.ssl.SSLContexts;
 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
@@ -36,16 +52,6 @@ import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
 import org.onap.portalsdk.core.util.SystemProperties;
 import org.onap.vid.properties.VidProperties;
 
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.*;
-import java.security.cert.CertificateException;
-import java.util.Map;
-
 public class SyncRestClient implements SyncRestClientInterface {
     private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
     private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
@@ -151,11 +157,11 @@ public class SyncRestClient implements SyncRestClientInterface {
 
     private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
         try {
-            return httpRequest.apply(url);
+            return patched(httpRequest.apply(url));
         } catch (RestClientException e) {
             if (causedBySslHandshakeError(e)) {
                 logger.warn(EELFLoggerDelegate.debugLogger, "SSL Handshake problem occured. Will try to retry over Http.", e);
-                return httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA));
+                return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)));
             }
             throw e;
         }
@@ -172,7 +178,7 @@ public class SyncRestClient implements SyncRestClientInterface {
             @Override
             public <T> T readValue(String value, Class<T> aClass) {
                 try {
-                    return objectMapper.readValue(value, aClass);
+                    return isEmpty(value) ? null : objectMapper.readValue(value, aClass);
                 } catch (IOException e) {
                     throw new SyncRestClientException("IOException while reading value", e);
                 }
diff --git a/vid-app-common/src/main/java/org/onap/vid/client/UnirestPatch.kt b/vid-app-common/src/main/java/org/onap/vid/client/UnirestPatch.kt
new file mode 100644 (file)
index 0000000..7506466
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * VID
+ * ================================================================================
+ * 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.
+ * 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.Headers
+import io.joshworks.restclient.http.HttpResponse
+import org.apache.http.HttpVersion
+import org.apache.http.message.BasicHttpResponse
+import java.io.InputStream
+
+/// Patch NPE in joshworks's Unirest HttpResponse::getBody when getRawBody is null
+fun <T> patched(httpResponse: HttpResponse<T>) =
+        if (willGetBodyTriggerNPE(httpResponse)) HttpResponsePatch(httpResponse) else httpResponse
+
+private fun <T> willGetBodyTriggerNPE(httpResponse: HttpResponse<T>) =
+        httpResponse.rawBody == null || httpResponse.rawBody.available() == 0
+
+private val dummyHttpResponse = BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "ok")
+
+/**
+ * This class inherits HttpResponse to have compatible interface,
+ * but implementation is done through delegation to another
+ * instance.
+ * For that, it's enough to pass dummy values to HttpResponse's
+ * constructor, as parent HttpResponse methods won't be used,
+ * only overridden.
+ */
+private class HttpResponsePatch<T>(private val delegatee: HttpResponse<T>) : HttpResponse<T>(
+        dummyHttpResponse, null, null
+) {
+    override fun getBody(): T? = if (willGetBodyTriggerNPE(delegatee)) null else delegatee.body
+    override fun getHeaders(): Headers? = delegatee.headers
+    override fun getStatus() = delegatee.status
+    override fun isSuccessful() = delegatee.isSuccessful
+    override fun getStatusText(): String? = delegatee.statusText
+    override fun getRawBody(): InputStream? = delegatee.rawBody
+}
index fc656fc..8d55c62 100644 (file)
 
 package org.onap.vid.controller;
 
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.module.kotlin.KotlinModule;
 import io.joshworks.restclient.http.mapper.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import javax.servlet.ServletContext;
 import org.onap.portalsdk.core.util.SystemProperties;
-import org.onap.vid.aai.*;
+import org.onap.vid.aai.AaiClient;
+import org.onap.vid.aai.AaiClientInterface;
+import org.onap.vid.aai.AaiOverTLSClient;
+import org.onap.vid.aai.AaiOverTLSClientInterface;
+import org.onap.vid.aai.AaiOverTLSPropertySupplier;
+import org.onap.vid.aai.AaiResponseTranslator;
+import org.onap.vid.aai.PombaClientImpl;
+import org.onap.vid.aai.PombaClientInterface;
+import org.onap.vid.aai.PombaRestInterface;
 import org.onap.vid.aai.model.PortDetailsTranslator;
-import org.onap.vid.aai.util.*;
+import org.onap.vid.aai.util.AAIRestInterface;
+import org.onap.vid.aai.util.CacheProvider;
+import org.onap.vid.aai.util.HttpsAuthClient;
+import org.onap.vid.aai.util.SSLContextProvider;
+import org.onap.vid.aai.util.ServiceInstanceStandardQuery;
+import org.onap.vid.aai.util.ServletRequestHelper;
+import org.onap.vid.aai.util.SystemPropertyHelper;
 import org.onap.vid.asdc.AsdcClient;
 import org.onap.vid.asdc.parser.ToscaParserImpl2;
 import org.onap.vid.asdc.parser.VidNotionsBuilder;
@@ -37,7 +56,13 @@ import org.onap.vid.client.SyncRestClientInterface;
 import org.onap.vid.properties.AsdcClientConfiguration;
 import org.onap.vid.scheduler.SchedulerService;
 import org.onap.vid.scheduler.SchedulerServiceImpl;
-import org.onap.vid.services.*;
+import org.onap.vid.services.AAIServiceTree;
+import org.onap.vid.services.AAITreeNodeBuilder;
+import org.onap.vid.services.AaiService;
+import org.onap.vid.services.AaiServiceImpl;
+import org.onap.vid.services.ChangeManagementService;
+import org.onap.vid.services.PombaService;
+import org.onap.vid.services.PombaServiceImpl;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -48,11 +73,6 @@ import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spring.web.plugins.Docket;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
-
-import javax.servlet.ServletContext;
-import java.io.File;
-import java.io.IOException;
-
 @EnableSwagger2
 @Configuration
 public class WebConfig {
@@ -177,7 +197,7 @@ public class WebConfig {
             @Override
             public <T> T readValue(String s, Class<T> aClass) {
                 try {
-                    return objectMapper.readValue(s, aClass);
+                    return isEmpty(s) ? null : objectMapper.readValue(s, aClass);
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 }