diff options
author | sourabh_sourabh <sourabh.sourabh@est.tech> | 2024-05-27 18:28:10 +0100 |
---|---|---|
committer | Sourabh Sourabh <sourabh.sourabh@est.tech> | 2024-06-06 15:54:57 +0000 |
commit | 43d0451a27311e4154536a66cb22eb4d68a21e7a (patch) | |
tree | eaef3f668b77ed49849410b21d7542ab912aee34 /cps-ncmp-service/src/main | |
parent | d7fa9601a1409ee3a156ac2f6a6ec11853989cd7 (diff) |
#1: Dedicated web client instance is assigned for data, model and health services
- Switched web client instance based on dmi service type (like data or model)
- 3 diff. beans are configured for data, model and health dmi service.
- Added configurable properties for data and model.
- Hard coded properties are assigned for health service.
Issue-ID: CPS-2231
Change-Id: I39fb739c07c41430dae43509fe29ece5306b7d71
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Diffstat (limited to 'cps-ncmp-service/src/main')
5 files changed, 168 insertions, 60 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index ef48c43d2a..7878c5d0ba 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -26,6 +26,7 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_D import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.REQUEST_TIMEOUT; import com.fasterxml.jackson.databind.JsonNode; import java.net.URI; @@ -37,7 +38,9 @@ import org.onap.cps.ncmp.api.impl.config.DmiProperties; import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException; import org.onap.cps.ncmp.api.impl.exception.InvalidDmiResourceUrlException; import org.onap.cps.ncmp.api.impl.operations.OperationType; +import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -45,6 +48,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; import org.springframework.web.reactive.function.client.WebClientResponseException; @Component @@ -55,31 +59,43 @@ public class DmiRestClient { private static final String HEALTH_CHECK_URL_EXTENSION = "/actuator/health"; private static final String NOT_SPECIFIED = ""; private static final String NO_AUTHORIZATION = null; - private final WebClient webClient; + private final DmiProperties dmiProperties; private final JsonObjectMapper jsonObjectMapper; + @Qualifier("dataServicesWebClient") + private final WebClient dataServicesWebClient; + @Qualifier("modelServicesWebClient") + private final WebClient modelServicesWebClient; + @Qualifier("healthChecksWebClient") + private final WebClient healthChecksWebClient; /** - * Sends POST operation to DMI with json body containing module references. + * Sends a POST operation to the DMI with a JSON body containing module references. * - * @param dmiResourceUrl dmi resource url - * @param requestBodyAsJsonString json data body - * @param operationType the type of operation being executed (for error reporting only) - * @param authorization contents of Authorization header, or null if not present - * @return response entity of type String + * @param requiredDmiService Determines if the required service is for a data or model operation. + * @param dmiUrl The DMI resource URL. + * @param requestBodyAsJsonString JSON data body. + * @param operationType The type of operation being executed (for error reporting only). + * @param authorization Contents of the Authorization header, or null if not present. + * @return ResponseEntity containing the response from the DMI. + * @throws DmiClientRequestException If there is an error during the DMI request. */ - public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl, + public ResponseEntity<Object> postOperationWithJsonData(final RequiredDmiService requiredDmiService, + final String dmiUrl, final String requestBodyAsJsonString, final OperationType operationType, final String authorization) { + final WebClient webClient = requiredDmiService.equals(RequiredDmiService.DATA) + ? dataServicesWebClient : modelServicesWebClient; try { - return ResponseEntity.ok(webClient.post().uri(toUri(dmiResourceUrl)) + return webClient.post() + .uri(toUri(dmiUrl)) .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization)) .body(BodyInserters.fromValue(requestBodyAsJsonString)) .retrieve() - .bodyToMono(Object.class) + .toEntity(Object.class) .onErrorMap(httpError -> handleDmiClientException(httpError, operationType.getOperationName())) - .block()); + .block(); } catch (final HttpServerErrorException e) { throw handleDmiClientException(e, operationType.getOperationName()); } @@ -88,31 +104,31 @@ public class DmiRestClient { /** * Get DMI plugin health status. * - * @param dmiPluginBaseUrl the base URL of the dmi-plugin - * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception) + * @param dmiUrl the base URL of the dmi-plugin + * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception) */ - public String getDmiHealthStatus(final String dmiPluginBaseUrl) { + public String getDmiHealthStatus(final String dmiUrl) { try { - final JsonNode responseHealthStatus = webClient.get() - .uri(toUri(dmiPluginBaseUrl + HEALTH_CHECK_URL_EXTENSION)) + final URI dmiHealthCheckUri = toUri(dmiUrl + HEALTH_CHECK_URL_EXTENSION); + final JsonNode responseHealthStatus = healthChecksWebClient.get() + .uri(dmiHealthCheckUri) .headers(httpHeaders -> configureHttpHeaders(httpHeaders, NO_AUTHORIZATION)) .retrieve() .bodyToMono(JsonNode.class).block(); return responseHealthStatus == null ? NOT_SPECIFIED : - responseHealthStatus.get("status").asText(); + responseHealthStatus.path("status").asText(); } catch (final Exception e) { - log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiPluginBaseUrl, e.getMessage()); + log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiUrl, e.getMessage()); return NOT_SPECIFIED; } } - private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) { + private void configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) { if (dmiProperties.isDmiBasicAuthEnabled()) { httpHeaders.setBasicAuth(dmiProperties.getAuthUsername(), dmiProperties.getAuthPassword()); } else if (authorization != null && authorization.toLowerCase(Locale.getDefault()).startsWith("bearer ")) { httpHeaders.add(HttpHeaders.AUTHORIZATION, authorization); } - return httpHeaders; } private static URI toUri(final String dmiResourceUrl) { @@ -123,22 +139,31 @@ public class DmiRestClient { } } - private DmiClientRequestException handleDmiClientException(final Throwable e, final String operationType) { - final String exceptionMessage = "Unable to " + operationType + " resource data."; - if (e instanceof WebClientResponseException wcre) { - if (wcre.getStatusCode().isSameCodeAs(HttpStatus.REQUEST_TIMEOUT)) { - throw new DmiClientRequestException(wcre.getStatusCode().value(), wcre.getMessage(), - jsonObjectMapper.asJsonString(wcre.getResponseBodyAsString()), DMI_SERVICE_NOT_RESPONDING); + private DmiClientRequestException handleDmiClientException(final Throwable throwable, final String operationType) { + if (throwable instanceof WebClientResponseException webClientResponseException) { + if (webClientResponseException.getStatusCode().isSameCodeAs(REQUEST_TIMEOUT)) { + throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(), + webClientResponseException.getMessage(), + jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()), + DMI_SERVICE_NOT_RESPONDING); } - throw new DmiClientRequestException(wcre.getStatusCode().value(), wcre.getMessage(), - jsonObjectMapper.asJsonString(wcre.getResponseBodyAsString()), UNABLE_TO_READ_RESOURCE_DATA); + throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(), + webClientResponseException.getMessage(), + jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()), + UNABLE_TO_READ_RESOURCE_DATA); } - if (e instanceof HttpServerErrorException httpServerErrorException) { + final String exceptionMessage = "Unable to " + operationType + " resource data."; + if (throwable instanceof WebClientRequestException webClientRequestException) { + throw new DmiClientRequestException(HttpStatus.SERVICE_UNAVAILABLE.value(), + webClientRequestException.getMessage(), + exceptionMessage, DMI_SERVICE_NOT_RESPONDING); + } + if (throwable instanceof HttpServerErrorException httpServerErrorException) { throw new DmiClientRequestException(httpServerErrorException.getStatusCode().value(), exceptionMessage, httpServerErrorException.getResponseBodyAsString(), DMI_SERVICE_NOT_RESPONDING); } - throw new DmiClientRequestException(INTERNAL_SERVER_ERROR.value(), exceptionMessage, e.getMessage(), + throw new DmiClientRequestException(INTERNAL_SERVER_ERROR.value(), exceptionMessage, throwable.getMessage(), UNKNOWN_ERROR); } } 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 2e84f7f69f..2c0b702627 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 @@ -34,6 +34,10 @@ import org.springframework.web.reactive.function.client.WebClient; import reactor.netty.http.client.HttpClient; 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. + */ @Configuration @RequiredArgsConstructor public class DmiWebClientConfiguration { @@ -41,30 +45,83 @@ public class DmiWebClientConfiguration { private final HttpClientConfiguration httpClientConfiguration; /** - * Configures and create 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 create a WebClient bean for DMI data service. + * + * @return a WebClient instance 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()); + } + + /** + * Configures and creates a WebClient bean for DMI model service. + * + * @return a WebClient instance 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()); + } + + /** + * Configures and creates a WebClient bean for DMI health service. * - * @return a WebClient instance. + * @return a WebClient instance for health checks. */ @Bean - public WebClient webClient() { - final ConnectionProvider dmiWebClientConnectionProvider = ConnectionProvider.create( - "dmiWebClientConnectionPool", httpClientConfiguration.getMaximumConnectionsTotal()); + 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()); + } + + 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); final HttpClient httpClient = HttpClient.create(dmiWebClientConnectionProvider) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, - httpClientConfiguration.getConnectionTimeoutInSeconds() * 1000) - .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler( - httpClientConfiguration.getReadTimeoutInSeconds(), TimeUnit.SECONDS)) - .addHandlerLast(new WriteTimeoutHandler( - httpClientConfiguration.getWriteTimeoutInSeconds(), TimeUnit.SECONDS))); + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutInSeconds * 1000) + .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(readTimeoutInSeconds, + TimeUnit.SECONDS)).addHandlerLast(new WriteTimeoutHandler(writeTimeoutInSeconds, + TimeUnit.SECONDS))); httpClient.warmup().block(); + return httpClient; + } + + private static WebClient buildAndGetWebClient(final HttpClient httpClient, + final Integer maximumInMemorySizeInMegabytes) { return WebClient.builder() .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( - httpClientConfiguration.getMaximumInMemorySizeInMegabytes() * 1024 * 1024)) - .build(); + 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 d2521d9d1e..62432f6cae 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 @@ -30,9 +30,37 @@ import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "ncmp.dmi.httpclient") public class HttpClientConfiguration { - private Integer maximumConnectionsTotal = 100; - private Integer connectionTimeoutInSeconds = 30; - private Integer readTimeoutInSeconds = 30; - private Integer writeTimeoutInSeconds = 30; - private Integer maximumInMemorySizeInMegabytes = 1; + + private final DataServices dataServices = new DataServices(); + private final ModelServices modelServices = new ModelServices(); + private final HealthCheckServices healthCheckServices = new HealthCheckServices(); + + @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; + } + + @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; + } + + @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; + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 6370879094..978855569a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -23,6 +23,7 @@ package org.onap.cps.ncmp.api.impl.operations; import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; +import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; import io.micrometer.core.annotation.Timed; import java.util.Collection; @@ -87,13 +88,9 @@ public class DmiDataOperations { final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - - final String dmiUrl = getDmiResourceDataUrl(cmResourceAddress.datastoreName(), - yangModelCmHandle, - cmResourceAddress.resourceIdentifier(), - optionsParamInQuery, - topicParamInQuery); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, READ, authorization); + final String dmiUrl = getDmiResourceDataUrl(cmResourceAddress.datastoreName(), yangModelCmHandle, + cmResourceAddress.resourceIdentifier(), optionsParamInQuery, topicParamInQuery); + return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, authorization); } /** @@ -114,7 +111,7 @@ public class DmiDataOperations { final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); final String dmiUrl = getDmiResourceDataUrl(datastoreName, yangModelCmHandle, "/", null, null); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, READ, null); + return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, null); } /** @@ -171,7 +168,7 @@ public class DmiDataOperations { yangModelCmHandle); final String dmiUrl = getDmiResourceDataUrl(PASSTHROUGH_RUNNING.getDatastoreName(), yangModelCmHandle, resourceId, null, null); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType, authorization); + return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, operationType, authorization); } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { @@ -251,7 +248,8 @@ public class DmiDataOperations { .operations(dmiDataOperationRequestBodies).build(); final String dmiDataOperationRequestAsJsonString = jsonObjectMapper.asJsonString(dmiDataOperationRequest); try { - dmiRestClient.postOperationWithJsonData(dmiUrl, dmiDataOperationRequestAsJsonString, READ, authorization); + dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, dmiDataOperationRequestAsJsonString, READ, + authorization); } catch (final DmiClientRequestException e) { handleTaskCompletionException(e, dmiUrl, dmiDataOperationRequestBodies); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 78d27b54b6..77dfcb7a20 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -111,7 +111,7 @@ public class DmiModelOperations { .variablePathSegment("cmHandleId", cmHandle) .variablePathSegment("resourceName", resourceName) .build(dmiServiceName, dmiProperties.getDmiBasePath()); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, OperationType.READ, null); + return dmiRestClient.postOperationWithJsonData(MODEL, dmiUrl, jsonRequestBody, OperationType.READ, null); } private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, |