Handle high frequency (faster then DMI) of passthrough request in NCMP

- Added pendingAcquireMaxCount property into appliaction.yml.
- Added findbugs google annotations dependency.
- Supressed "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" spotbugs low error for
  reactor.netty.resources.ConnectionProvider.

Issue-ID: CPS-2262
Change-Id: Ie755e40282473933f2052fbe7654e7090bb9b337
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java
index 08885a9..3a861a6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java
@@ -20,9 +20,12 @@
 
 package org.onap.cps.ncmp.api.impl.config;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import io.netty.channel.ChannelOption;
 import io.netty.handler.timeout.ReadTimeoutHandler;
 import io.netty.handler.timeout.WriteTimeoutHandler;
+import io.netty.resolver.DefaultAddressResolverGroup;
+import java.time.Duration;
 import java.util.concurrent.TimeUnit;
 import lombok.RequiredArgsConstructor;
 import org.springframework.context.annotation.Bean;
@@ -35,8 +38,9 @@
 import reactor.netty.resources.ConnectionProvider;
 
 /**
- * Configures and creates a WebClient bean that triggers an initialization (warmup) of the host name resolver and
- * loads the necessary native libraries to avoid the extra time needed to load resources for first request.
+ * Configures and creates WebClient beans for various DMI services including data, model, and health check services.
+ * The configuration utilizes Netty-based HttpClient with custom connection settings, read and write timeouts,
+ * and initializes WebClient with these settings to ensure optimal performance and resource management.
  */
 @Configuration
 @RequiredArgsConstructor
@@ -44,82 +48,98 @@
 
     private final HttpClientConfiguration httpClientConfiguration;
 
+    private static final Duration DEFAULT_RESPONSE_TIMEOUT = Duration.ofSeconds(30);
+
     /**
-     * Configures and create a WebClient bean for DMI data service.
+     * Configures and creates a WebClient bean for DMI data services.
      *
-     * @return a WebClient instance for data services.
+     * @return a WebClient instance configured for data services.
      */
     @Bean
     public WebClient dataServicesWebClient() {
-        final HttpClientConfiguration.DataServices httpClientConfiguration
-                = this.httpClientConfiguration.getDataServices();
-
-        final HttpClient httpClient = createHttpClient("dataConnectionPool",
-                httpClientConfiguration.getMaximumConnectionsTotal(),
-                httpClientConfiguration.getConnectionTimeoutInSeconds(),
-                httpClientConfiguration.getReadTimeoutInSeconds(),
-                httpClientConfiguration.getWriteTimeoutInSeconds());
-        return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+        final HttpClientConfiguration.DataServices dataServiceConfig = httpClientConfiguration.getDataServices();
+        final ConnectionProvider dataServicesConnectionProvider
+                = getConnectionProvider(dataServiceConfig.getConnectionProviderName(),
+                dataServiceConfig.getMaximumConnectionsTotal(), dataServiceConfig.getPendingAcquireMaxCount());
+        final HttpClient dataServicesHttpClient = createHttpClient(dataServiceConfig, dataServicesConnectionProvider);
+        return buildAndGetWebClient(dataServicesHttpClient, dataServiceConfig.getMaximumInMemorySizeInMegabytes());
     }
 
     /**
-     * Configures and creates a WebClient bean for DMI model service.
+     * Configures and creates a WebClient bean for DMI model services.
      *
-     * @return a WebClient instance for model services.
+     * @return a WebClient instance configured for model services.
      */
     @Bean
     public WebClient modelServicesWebClient() {
-        final HttpClientConfiguration.ModelServices httpClientConfiguration
-                = this.httpClientConfiguration.getModelServices();
-
-        final HttpClient httpClient = createHttpClient("modelConnectionPool",
-                httpClientConfiguration.getMaximumConnectionsTotal(),
-                httpClientConfiguration.getConnectionTimeoutInSeconds(),
-                httpClientConfiguration.getReadTimeoutInSeconds(),
-                httpClientConfiguration.getWriteTimeoutInSeconds());
-        return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+        final HttpClientConfiguration.ModelServices modelServiceConfig = httpClientConfiguration.getModelServices();
+        final ConnectionProvider modelServicesConnectionProvider
+                = getConnectionProvider(modelServiceConfig.getConnectionProviderName(),
+                modelServiceConfig.getMaximumConnectionsTotal(),
+                modelServiceConfig.getPendingAcquireMaxCount());
+        final HttpClient modelServicesHttpClient
+                = createHttpClient(modelServiceConfig, modelServicesConnectionProvider);
+        return buildAndGetWebClient(modelServicesHttpClient, modelServiceConfig.getMaximumInMemorySizeInMegabytes());
     }
 
     /**
-     * Configures and creates a WebClient bean for DMI health service.
+     * Configures and creates a WebClient bean for DMI health check services.
      *
-     * @return a WebClient instance for health checks.
+     * @return a WebClient instance configured for health check services.
      */
     @Bean
     public WebClient healthChecksWebClient() {
-        final HttpClientConfiguration.HealthCheckServices httpClientConfiguration
-                = this.httpClientConfiguration.getHealthCheckServices();
-
-        final HttpClient httpClient = createHttpClient("healthConnectionPool",
-                httpClientConfiguration.getMaximumConnectionsTotal(),
-                httpClientConfiguration.getConnectionTimeoutInSeconds(),
-                httpClientConfiguration.getReadTimeoutInSeconds(),
-                httpClientConfiguration.getWriteTimeoutInSeconds());
-        return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+        final HttpClientConfiguration.HealthCheckServices healthCheckServiceConfig
+                = httpClientConfiguration.getHealthCheckServices();
+        final ConnectionProvider healthChecksConnectionProvider
+                = getConnectionProvider(healthCheckServiceConfig.getConnectionProviderName(),
+                healthCheckServiceConfig.getMaximumConnectionsTotal(),
+                healthCheckServiceConfig.getPendingAcquireMaxCount());
+        final HttpClient healthChecksHttpClient
+                = createHttpClient(healthCheckServiceConfig, healthChecksConnectionProvider);
+        return buildAndGetWebClient(healthChecksHttpClient,
+                healthCheckServiceConfig.getMaximumInMemorySizeInMegabytes());
     }
 
-    private static HttpClient createHttpClient(final String connectionProviderName,
-                                               final Integer maximumConnectionsTotal,
-                                               final Integer connectionTimeoutInSeconds,
-                                               final Integer readTimeoutInSeconds,
-                                               final Integer writeTimeoutInSeconds) {
-        final ConnectionProvider dmiWebClientConnectionProvider = ConnectionProvider.create(connectionProviderName,
-                maximumConnectionsTotal);
-
-        return HttpClient.create(dmiWebClientConnectionProvider)
-                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutInSeconds * 1000)
-                .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(readTimeoutInSeconds,
-                        TimeUnit.SECONDS)).addHandlerLast(new WriteTimeoutHandler(writeTimeoutInSeconds,
-                        TimeUnit.SECONDS)));
+    /**
+     * Provides a WebClient.Builder bean for creating WebClient instances.
+     *
+     * @return a WebClient.Builder instance.
+     */
+    @Bean
+    public WebClient.Builder webClientBuilder() {
+        return WebClient.builder();
     }
 
-    private static WebClient buildAndGetWebClient(final HttpClient httpClient,
-                                                  final Integer maximumInMemorySizeInMegabytes) {
-        return WebClient.builder()
+    private static HttpClient createHttpClient(final HttpClientConfiguration.ServiceConfig serviceConfig,
+                                               final ConnectionProvider connectionProvider) {
+        return HttpClient.create(connectionProvider)
+                .responseTimeout(DEFAULT_RESPONSE_TIMEOUT)
+                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serviceConfig.getConnectionTimeoutInSeconds() * 1000)
+                .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(
+                        serviceConfig.getReadTimeoutInSeconds(), TimeUnit.SECONDS)).addHandlerLast(
+                        new WriteTimeoutHandler(serviceConfig.getWriteTimeoutInSeconds(), TimeUnit.SECONDS)))
+                .resolver(DefaultAddressResolverGroup.INSTANCE)
+                .compress(true);
+    }
+
+    @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
+    private static ConnectionProvider getConnectionProvider(final String connectionProviderName,
+                                                            final int maximumConnectionsTotal,
+                                                            final int pendingAcquireMaxCount) {
+        return ConnectionProvider.builder(connectionProviderName)
+                .maxConnections(maximumConnectionsTotal)
+                .pendingAcquireMaxCount(pendingAcquireMaxCount)
+                .build();
+    }
+
+    private WebClient buildAndGetWebClient(final HttpClient httpClient,
+                                                  final int maximumInMemorySizeInMegabytes) {
+        return webClientBuilder()
                 .defaultHeaders(header -> header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
                 .defaultHeaders(header -> header.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE))
                 .clientConnector(new ReactorClientHttpConnector(httpClient))
-                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(
-                        maximumInMemorySizeInMegabytes * 1024 * 1024)).build();
+                .codecs(configurer -> configurer.defaultCodecs()
+                        .maxInMemorySize(maximumInMemorySizeInMegabytes * 1024 * 1024)).build();
     }
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
index 62432f6..0acbabb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
@@ -23,11 +23,11 @@
 import lombok.Getter;
 import lombok.Setter;
 import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
+import org.springframework.context.annotation.Configuration;
 
 @Getter
 @Setter
-@Component
+@Configuration
 @ConfigurationProperties(prefix = "ncmp.dmi.httpclient")
 public class HttpClientConfiguration {
 
@@ -37,30 +37,36 @@
 
     @Getter
     @Setter
-    public static class DataServices {
-        private Integer maximumConnectionsTotal = 100;
-        private Integer connectionTimeoutInSeconds = 30;
-        private Integer readTimeoutInSeconds = 30;
-        private Integer writeTimeoutInSeconds = 30;
-        private Integer maximumInMemorySizeInMegabytes = 1;
+    public static class DataServices extends ServiceConfig {
+        private String connectionProviderName = "dataConnectionPool";
     }
 
     @Getter
     @Setter
-    public static class ModelServices {
-        private Integer maximumConnectionsTotal = 100;
-        private Integer connectionTimeoutInSeconds = 30;
-        private Integer readTimeoutInSeconds = 30;
-        private Integer writeTimeoutInSeconds = 30;
-        private Integer maximumInMemorySizeInMegabytes = 1;
+    public static class ModelServices extends ServiceConfig {
+        private String connectionProviderName = "modelConnectionPool";
     }
 
     @Getter
-    public static class HealthCheckServices {
-        private final Integer maximumConnectionsTotal = 10;
-        private final Integer connectionTimeoutInSeconds = 30;
-        private final Integer readTimeoutInSeconds = 30;
-        private final Integer writeTimeoutInSeconds = 30;
-        private final Integer maximumInMemorySizeInMegabytes = 1;
+    @Setter
+    public static class HealthCheckServices extends ServiceConfig {
+        private String connectionProviderName = "healthConnectionPool";
+        private int maximumConnectionsTotal = 10;
+        private int pendingAcquireMaxCount = 5;
+    }
+
+    /**
+     * Base configuration properties for all services.
+     */
+    @Getter
+    @Setter
+    public static class ServiceConfig {
+        private String connectionProviderName = "cpsConnectionPool";
+        private int maximumConnectionsTotal = 100;
+        private int pendingAcquireMaxCount = 50;
+        private Integer connectionTimeoutInSeconds = 30;
+        private long readTimeoutInSeconds = 30;
+        private long writeTimeoutInSeconds = 30;
+        private int maximumInMemorySizeInMegabytes = 1;
     }
 }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
index b7ced23..228f412 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
@@ -43,6 +43,7 @@
                 assert readTimeoutInSeconds == 789
                 assert writeTimeoutInSeconds == 30
                 assert maximumConnectionsTotal == 100
+                assert pendingAcquireMaxCount == 22
                 assert maximumInMemorySizeInMegabytes == 7
             }
     }
@@ -54,6 +55,7 @@
                 assert readTimeoutInSeconds == 30
                 assert writeTimeoutInSeconds == 30
                 assert maximumConnectionsTotal == 111
+                assert pendingAcquireMaxCount == 44
                 assert maximumInMemorySizeInMegabytes == 8
             }
     }
@@ -65,6 +67,7 @@
                 assert readTimeoutInSeconds == 30
                 assert writeTimeoutInSeconds == 30
                 assert maximumConnectionsTotal == 10
+                assert pendingAcquireMaxCount == 5
                 assert maximumInMemorySizeInMegabytes == 1
             }
     }
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index e35f471..5b10e73 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -38,9 +38,11 @@
     dmi:
         httpclient:
             data-services:
+                pendingAcquireMaxCount: 22
                 connectionTimeoutInSeconds: 123
                 maximumInMemorySizeInMegabytes: 7
             model-services:
+                pendingAcquireMaxCount: 44
                 connectionTimeoutInSeconds: 456
                 maximumInMemorySizeInMegabytes: 8
         auth: