diff options
5 files changed, 180 insertions, 30 deletions
diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml index 7118a616..64cdeb5b 100644 --- a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml +++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml @@ -94,6 +94,10 @@ <version>3.4.9</version> </dependency> <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> + <dependency> <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-annotations</artifactId> </dependency> diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java index 5021ae76..d706e626 100644 --- a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java +++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java @@ -20,7 +20,6 @@ package org.onap.cps.ncmp.dmi.rest.stub.controller; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -40,6 +39,7 @@ import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.onap.cps.ncmp.dmi.datajobs.model.SubjobWriteRequest; import org.onap.cps.ncmp.dmi.datajobs.model.SubjobWriteResponse; +import org.onap.cps.ncmp.dmi.rest.stub.controller.aop.ModuleInitialProcess; import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest; import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest; import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiOperationCmHandle; @@ -72,7 +72,6 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor public class DmiRestStubController { - private static final String DEFAULT_TAG = "tagD"; private static final String DEFAULT_PASSTHROUGH_OPERATION = "read"; private static final String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent"; private static final Map<String, String> moduleSetTagPerCmHandleId = new HashMap<>(); @@ -161,23 +160,14 @@ public class DmiRestStubController { * @return ResponseEntity response entity having module response as json string. */ @PostMapping("/v1/ch/{cmHandleId}/modules") + @ModuleInitialProcess public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId, @RequestBody final Object moduleReferencesRequest) { - delay(moduleReferencesDelayMs); - try { - log.info("Incoming DMI request body: {}", - objectMapper.writeValueAsString(moduleReferencesRequest)); - } catch (final JsonProcessingException jsonProcessingException) { - log.info("Unable to parse dmi data operation request to json string"); - } - final String moduleResponseContent = getModuleResourceResponse(cmHandleId, - "ModuleResponse.json"); - log.info("cm handle: {} requested for modules", cmHandleId); - return ResponseEntity.ok(moduleResponseContent); + return processModuleRequest(moduleReferencesRequest, "ModuleResponse.json", moduleReferencesDelayMs); } /** - * Retrieves module resources for a given cmHandleId. + * Get module resources for a given cmHandleId. * * @param cmHandleId The identifier for a network function, network element, subnetwork, * or any other cm object by managed Network CM Proxy @@ -185,14 +175,11 @@ public class DmiRestStubController { * @return ResponseEntity response entity having module resources response as json string. */ @PostMapping("/v1/ch/{cmHandleId}/moduleResources") - public ResponseEntity<String> retrieveModuleResources( + @ModuleInitialProcess + public ResponseEntity<String> getModuleResources( @PathVariable("cmHandleId") final String cmHandleId, @RequestBody final Object moduleResourcesReadRequest) { - delay(moduleResourcesDelayMs); - final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId, - "ModuleResourcesResponse.json"); - log.info("cm handle: {} requested for modules resources", cmHandleId); - return ResponseEntity.ok(moduleResourcesResponseContent); + return processModuleRequest(moduleResourcesReadRequest, "ModuleResourcesResponse.json", moduleResourcesDelayMs); } /** @@ -363,18 +350,39 @@ public class DmiRestStubController { return dataOperationEvent; } - private String getModuleResourceResponse(final String cmHandleId, final String moduleResponseType) { - if (moduleSetTagPerCmHandleId.isEmpty()) { - log.info("Using default module responses of type ietfYang"); - return ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource( - ResourceLoader.CLASSPATH_URL_PREFIX - + String.format("module/ietfYang-%s", moduleResponseType))); + private ResponseEntity<String> processModuleRequest(Object moduleRequest, String responseFileName, long simulatedResponseDelay) { + String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest); + logRequestBody(moduleRequest); + String moduleResponseContent = getModuleResponseContent(moduleSetTag, responseFileName); + delay(simulatedResponseDelay); + return ResponseEntity.ok(moduleResponseContent); + } + + private String extractModuleSetTagFromRequest(Object moduleReferencesRequest) { + JsonNode rootNode = objectMapper.valueToTree(moduleReferencesRequest); + return rootNode.path("moduleSetTag").asText(null); + } + + private boolean isModuleSetTagNullOrEmpty(String moduleSetTag) { + return moduleSetTag == null || moduleSetTag.trim().isEmpty(); + } + + private void logRequestBody(Object request) { + try { + log.info("Incoming DMI request body: {}", objectMapper.writeValueAsString(request)); + } catch (final JsonProcessingException jsonProcessingException) { + log.info("Unable to parse DMI request to json string"); } - final String moduleSetTag = moduleSetTagPerCmHandleId.getOrDefault(cmHandleId, DEFAULT_TAG); - final String moduleResponseFilePath = String.format("module/%s-%s", moduleSetTag, moduleResponseType); - final Resource moduleResponseResource = applicationContext.getResource( - ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath); + } + + private String getModuleResponseContent(final String moduleSetTag, final String responseFileName) { + String moduleResponseFilePath = isModuleSetTagNullOrEmpty(moduleSetTag) + ? String.format("module/ietfYang-%s", responseFileName) + : String.format("module/%s-%s", moduleSetTag, responseFileName); log.info("Using module responses from : {}", moduleResponseFilePath); + + Resource moduleResponseResource = applicationContext.getResource( + ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath); return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource); } diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java new file mode 100644 index 00000000..8444dd58 --- /dev/null +++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.dmi.rest.stub.controller.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark methods that require initial processing for module set tag. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ModuleInitialProcess { +}
\ No newline at end of file diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java new file mode 100644 index 00000000..96f0b804 --- /dev/null +++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java @@ -0,0 +1,103 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.dmi.rest.stub.controller.aop; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Aspect to handle initial processing for methods annotated with @ModuleInitialProcess. + */ +@Slf4j +@Aspect +@Component +@RequiredArgsConstructor +public class ModuleInitialProcessAspect { + + private final ObjectMapper objectMapper; + private static final Map<String, Long> firstRequestTimePerModuleSetTag = new ConcurrentHashMap<>(); + + @Value("${delay.module-initial-processing-delay-ms:120000}") + private long moduleInitialProcessingDelayMs; + + /** + * Around advice to handle methods annotated with @ModuleInitialProcess. + * + * @param proceedingJoinPoint the join point representing the method execution + * @param moduleInitialProcess the annotation containing the module set tag + * @return the result of the method execution or a ResponseEntity indicating that the service is unavailable + */ + @Around("@annotation(moduleInitialProcess)") + public Object handleModuleInitialProcess(ProceedingJoinPoint proceedingJoinPoint, ModuleInitialProcess moduleInitialProcess) throws Throwable { + log.debug("Aspect invoked for method: {}", proceedingJoinPoint.getSignature()); + Object moduleRequest = proceedingJoinPoint.getArgs()[1]; + String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest); + + if (isModuleSetTagEmptyOrInvalid(moduleSetTag)) { + log.debug("Received request with an empty or null moduleSetTag. Returning default processing."); + return proceedingJoinPoint.proceed(); + } + + long firstRequestTimestamp = getFirstRequestTimestamp(moduleSetTag); + long currentTimestamp = System.currentTimeMillis(); + + if (isInitialProcessingCompleted(currentTimestamp, firstRequestTimestamp)) { + log.debug("Initial processing for moduleSetTag '{}' is completed.", moduleSetTag); + return proceedingJoinPoint.proceed(); + } + + long remainingProcessingTime = calculateRemainingProcessingTime(currentTimestamp, firstRequestTimestamp); + log.info("Initial processing for moduleSetTag '{}' is still active. Returning HTTP 503. Remaining time: {} ms.", moduleSetTag, remainingProcessingTime); + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build(); + } + + private String extractModuleSetTagFromRequest(Object moduleRequest) { + JsonNode rootNode = objectMapper.valueToTree(moduleRequest); + return rootNode.path("moduleSetTag").asText(null); + } + + private boolean isModuleSetTagEmptyOrInvalid(String moduleSetTag) { + return moduleSetTag == null || moduleSetTag.trim().isEmpty(); + } + + private long getFirstRequestTimestamp(String moduleSetTag) { + return firstRequestTimePerModuleSetTag.computeIfAbsent(moduleSetTag, firstRequestTime -> System.currentTimeMillis()); + } + + private boolean isInitialProcessingCompleted(long currentTimestamp, long firstRequestTimestamp) { + return currentTimestamp - firstRequestTimestamp > moduleInitialProcessingDelayMs; + } + + private long calculateRemainingProcessingTime(long currentTimestamp, long firstRequestTimestamp) { + return moduleInitialProcessingDelayMs - (currentTimestamp - firstRequestTimestamp); + } +} diff --git a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml index e1e33542..71bccc9a 100644 --- a/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml +++ b/dmi-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml @@ -53,6 +53,7 @@ app: delay: module-references-delay-ms: ${MODULE_REFERENCES_DELAY_MS:100} module-resources-delay-ms: ${MODULE_RESOURCES_DELAY_MS:1000} + module-initial-processing-delay-ms: ${MODULE_INITIAL_PROCESSING_DELAY_MS:120000} read-data-for-cm-handle-delay-ms: ${READ_DATA_FOR_CM_HANDLE_DELAY_MS:300} write-data-for-cm-handle-delay-ms: ${WRITE_DATA_FOR_CM_HANDLE_DELAY_MS:670} |