diff options
Diffstat (limited to 'cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api')
15 files changed, 772 insertions, 504 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java index 45d5bd911..ec816ed73 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java @@ -22,8 +22,9 @@ package org.onap.cps.ncmp.api; +import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; + import java.util.Collection; -import javax.validation.constraints.NotNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.spi.FetchDescendantsOption; @@ -118,8 +119,8 @@ public interface NetworkCmProxyDataService { * @param optionsParamInQuery options query * @return {@code Object} resource data */ - Object getResourceDataOperationalForCmHandle(@NotNull String cmHandle, - @NotNull String resourceIdentifier, + Object getResourceDataOperationalForCmHandle(String cmHandle, + String resourceIdentifier, String acceptParamInHeader, String optionsParamInQuery); @@ -133,24 +134,25 @@ public interface NetworkCmProxyDataService { * @param optionsParamInQuery options query * @return {@code Object} resource data */ - Object getResourceDataPassThroughRunningForCmHandle(@NotNull String cmHandle, - @NotNull String resourceIdentifier, + Object getResourceDataPassThroughRunningForCmHandle(String cmHandle, + String resourceIdentifier, String acceptParamInHeader, String optionsParamInQuery); /** - * Create resource data for data store pass-through running + * Write resource data for data store pass-through running * using dmi for given cm-handle. - * - * @param cmHandle cm handle + * @param cmHandle cm handle * @param resourceIdentifier resource identifier + * @param operation required operation * @param requestBody request body to create resource * @param contentType content type in body */ - void createResourceDataPassThroughRunningForCmHandle(@NotNull String cmHandle, - @NotNull String resourceIdentifier, - @NotNull String requestBody, - String contentType); + void writeResourceDataPassThroughRunningForCmHandle(String cmHandle, + String resourceIdentifier, + OperationEnum operation, + String requestBody, + String contentType); /** * Retrieve module references for the given cm handle. @@ -158,7 +160,7 @@ public interface NetworkCmProxyDataService { * @param cmHandle cm handle * @return a collection of modules names and revisions */ - Collection<ModuleReference> getYangResourcesModuleReferences(@NotNull String cmHandle); + Collection<ModuleReference> getYangResourcesModuleReferences(String cmHandle); /** * Query cm handle identifiers for the given collection of module names. @@ -169,14 +171,4 @@ public interface NetworkCmProxyDataService { */ Collection<String> executeCmHandleHasAllModulesSearch(Collection<String> moduleNames); - /** - * Update resource data for data store pass-through running using dmi for the given cm-handle. - * - * @param cmHandle cm handle - * @param resourceIdentifier resource identifier - * @param requestBody request body to create resource - * @param contentType content type in body - */ - void updateResourceDataPassThroughRunningForCmHandle(String cmHandle, String resourceIdentifier, - String requestBody, String contentType); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 80cd29728..fb929a234 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -23,6 +23,8 @@ package org.onap.cps.ncmp.api.impl; +import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; @@ -32,12 +34,9 @@ import com.google.gson.JsonObject; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; @@ -45,13 +44,12 @@ import org.onap.cps.api.CpsModuleService; import org.onap.cps.api.CpsQueryService; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.exception.NcmpException; -import org.onap.cps.ncmp.api.impl.operation.DmiOperations; +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; +import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations; +import org.onap.cps.ncmp.api.impl.operations.DmiOperations; import org.onap.cps.ncmp.api.models.CmHandle; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; -import org.onap.cps.ncmp.api.models.GenericRequestBody; -import org.onap.cps.ncmp.api.models.GenericRequestBody.OperationEnum; import org.onap.cps.ncmp.api.models.PersistenceCmHandle; -import org.onap.cps.ncmp.api.models.PersistenceCmHandle.AdditionalProperty; import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList; import org.onap.cps.ncmp.api.models.YangResource; import org.onap.cps.spi.FetchDescendantsOption; @@ -59,7 +57,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleReference; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -76,17 +73,15 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static final OffsetDateTime NO_TIMESTAMP = null; - private static final String NCMP_DMI_SERVICE_NAME = "dmi-service-name"; - - private static final String REVISION = "revision"; - private CpsDataService cpsDataService; private ObjectMapper objectMapper; private CpsQueryService cpsQueryService; - private DmiOperations dmiOperations; + private DmiDataOperations dmiDataOperations; + + private DmiModelOperations dmiModelOperations; private CpsModuleService cpsModuleService; @@ -94,18 +89,20 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService /** * Constructor Injection for Dependencies. - * @param dmiOperations DMI operation + * @param dmiDataOperations DMI operation * @param cpsDataService Data Service Interface * @param cpsQueryService Query Service Interface * @param objectMapper Object Mapper */ - public NetworkCmProxyDataServiceImpl(final DmiOperations dmiOperations, - final CpsModuleService cpsModuleService, - final CpsDataService cpsDataService, - final CpsQueryService cpsQueryService, - final CpsAdminService cpsAdminService, - final ObjectMapper objectMapper) { - this.dmiOperations = dmiOperations; + public NetworkCmProxyDataServiceImpl(final DmiDataOperations dmiDataOperations, + final DmiModelOperations dmiModelOperations, + final CpsModuleService cpsModuleService, + final CpsDataService cpsDataService, + final CpsQueryService cpsQueryService, + final CpsAdminService cpsAdminService, + final ObjectMapper objectMapper) { + this.dmiDataOperations = dmiDataOperations; + this.dmiModelOperations = dmiModelOperations; this.cpsModuleService = cpsModuleService; this.cpsDataService = cpsDataService; this.cpsQueryService = cpsQueryService; @@ -158,9 +155,10 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService @Override public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) { + dmiPluginRegistration.validateDmiPluginRegistration(); try { if (dmiPluginRegistration.getCreatedCmHandles() != null) { - parseAndCreateCmHandlesInDmiRegistrationAndSyncModule(dmiPluginRegistration); + parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration); } if (dmiPluginRegistration.getUpdatedCmHandles() != null) { parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration); @@ -174,64 +172,44 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, + public Object getResourceDataOperationalForCmHandle(final String cmHandle, + final String resourceIdentifier, final String acceptParamInHeader, final String optionsParamInQuery) { - - final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); - final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME)); - final String dmiRequestBody = getGenericRequestBody(cmHandleDataNode); - final ResponseEntity<Object> response = dmiOperations.getResourceDataOperationalFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - optionsParamInQuery, - acceptParamInHeader, - dmiRequestBody); - return handleResponse(response); + return handleResponse(dmiDataOperations.getResourceDataFromDmi( + cmHandle, + resourceIdentifier, + optionsParamInQuery, + acceptParamInHeader, + DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL), "Not able to get resource data."); } @Override - public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, + public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandle, + final String resourceIdentifier, final String acceptParamInHeader, final String optionsParamInQuery) { - final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); - final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME)); - final String dmiRequestBody = getGenericRequestBody(cmHandleDataNode); - final ResponseEntity<Object> response = dmiOperations.getResourceDataPassThroughRunningFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - optionsParamInQuery, - acceptParamInHeader, - dmiRequestBody); - return handleResponse(response); + return handleResponse(dmiDataOperations.getResourceDataFromDmi( + cmHandle, + resourceIdentifier, + optionsParamInQuery, + acceptParamInHeader, + DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING), "Not able to get resource data."); } @Override - public void createResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final @NotNull String requestBody, - final String contentType) { - final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); - final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME)); - final Collection<DataNode> cmHandlePropertiesAsDataNodes = cmHandleDataNode.getChildDataNodes(); - final Map<String, String> cmHandlePropertiesAsMap = getCmHandlePropertiesAsMap(cmHandlePropertiesAsDataNodes); - final GenericRequestBody dmiRequestBodyObject = GenericRequestBody.builder() - .operation(GenericRequestBody.OperationEnum.CREATE) - .dataType(contentType) - .data(requestBody) - .cmHandleProperties(cmHandlePropertiesAsMap) - .build(); - final String dmiRequestBody = prepareOperationBody(dmiRequestBodyObject); - final ResponseEntity<String> responseEntity = dmiOperations - .createResourceDataPassThroughRunningFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - dmiRequestBody); - handleResponseFromDmi(responseEntity, "Not able to create resource data."); + public void writeResourceDataPassThroughRunningForCmHandle(final String cmHandle, + final String resourceIdentifier, + final OperationEnum operation, + final String requestData, + final String dataType) { + handleResponse( + dmiDataOperations.writeResourceDataPassThroughRunningFromDmi( + cmHandle, resourceIdentifier, operation, requestData, dataType), + "Not able to " + operation + " resource data."); } + @Override public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandle) { return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandle); @@ -249,134 +227,46 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } /** - * Update resource data for data store pass-through running using dmi for given cm-handle. + * THis method registers a cm handle and intiates modules sync. * - * @param cmHandle cm handle - * @param resourceIdentifier resource identifier - * @param requestBody request body to create resource - * @param contentType content type in body + * @param dmiPluginRegistration dmi plugin registration information. + * @throws JsonProcessingException thrown if json is malformed or missing. */ - @Override - public void updateResourceDataPassThroughRunningForCmHandle(final String cmHandle, final String resourceIdentifier, - final String requestBody, final String contentType) { - final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); - final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME)); - final Collection<DataNode> cmHandlePropertiesAsDataNodes = cmHandleDataNode.getChildDataNodes(); - final Map<String, String> cmHandlePropertiesAsMap = getCmHandlePropertiesAsMap(cmHandlePropertiesAsDataNodes); - final GenericRequestBody dmiRequestBodyObject = GenericRequestBody.builder() - .operation(OperationEnum.UPDATE) - .dataType(contentType) - .data(requestBody) - .cmHandleProperties(cmHandlePropertiesAsMap) - .build(); - final String dmiRequestBody = prepareOperationBody(dmiRequestBodyObject); - final ResponseEntity<String> responseEntity = dmiOperations - .updateResourceDataPassThroughRunningFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - dmiRequestBody); - handleResponseFromDmi(responseEntity, "Unable to replace resource data."); - } - - private DataNode fetchDataNodeFromDmiRegistryForCmHandle(final String cmHandle) { - final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']"; - return cpsDataService.getDataNode(NCMP_DATASPACE_NAME, - NCMP_DMI_REGISTRY_ANCHOR, - xpathForDmiRegistryToFetchCmHandle, - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); - } - - private String prepareOperationBody(final GenericRequestBody requestBodyObject) { - try { - return objectMapper.writeValueAsString(requestBodyObject); - } catch (final JsonProcessingException e) { - log.error("Parsing error occurred while converting Object to JSON."); - throw new NcmpException("Parsing error occurred while converting given object to JSON.", - e.getMessage()); - } - } - - private static Map<String, String> getCmHandlePropertiesAsMap( - final Collection<DataNode> cmHandlePropertiesAsDataNode) { - if (cmHandlePropertiesAsDataNode.isEmpty()) { - return Collections.emptyMap(); - } - final Map<String, String> cmHandlePropertiesAsMap = new LinkedHashMap<>(); - for (final DataNode dataNode: cmHandlePropertiesAsDataNode) { - cmHandlePropertiesAsMap.put(String.valueOf(dataNode.getLeaves().get("name")), - String.valueOf(dataNode.getLeaves().get("value"))); - } - return cmHandlePropertiesAsMap; - } - - private static Map<String, String> getCmHandlePropertiesAsMap( - final List<AdditionalProperty> cmHandlePropertiesAsList) { - if (cmHandlePropertiesAsList == null || cmHandlePropertiesAsList.isEmpty()) { - return Collections.emptyMap(); - } - final Map<String, String> cmHandlePropertiesAsMap = new LinkedHashMap<>(); - for (final AdditionalProperty additionalProperty: cmHandlePropertiesAsList) { - cmHandlePropertiesAsMap.put(additionalProperty.getName(), - additionalProperty.getValue()); - } - return cmHandlePropertiesAsMap; + public void parseAndCreateCmHandlesInDmiRegistrationAndSyncModules( + final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException { + final PersistenceCmHandlesList createdPersistenceCmHandlesList = + getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getCreatedCmHandles()); + registerAndSyncNewCmHandles(createdPersistenceCmHandlesList); } - private static Object handleResponse(final @NotNull ResponseEntity<Object> responseEntity) { - if (responseEntity.getStatusCode() == HttpStatus.OK) { + private static Object handleResponse(final ResponseEntity<?> responseEntity, + final String exceptionMessage) { + if (responseEntity.getStatusCode().is2xxSuccessful()) { return responseEntity.getBody(); } else { - throw new NcmpException("Not able to get resource data.", + throw new NcmpException(exceptionMessage, "DMI status code: " + responseEntity.getStatusCodeValue() + ", DMI response body: " + responseEntity.getBody()); } } - private static void handleResponseFromDmi(final @NotNull ResponseEntity<String> responseEntity, - final String exceptionMessage) { - if (!HttpStatus.valueOf(responseEntity.getStatusCodeValue()).is2xxSuccessful()) { - throw new NcmpException(exceptionMessage, - "DMI status code: " + responseEntity.getStatusCodeValue() - + ", DMI response body: " + responseEntity.getBody()); - } - } - - private String getGenericRequestBody(final DataNode cmHandleDataNode) { - final Collection<DataNode> cmHandlePropertiesAsDataNodes = cmHandleDataNode.getChildDataNodes(); - final Map<String, String> cmHandlePropertiesAsMap = getCmHandlePropertiesAsMap(cmHandlePropertiesAsDataNodes); - final GenericRequestBody requestBodyObject = GenericRequestBody.builder() - .operation(GenericRequestBody.OperationEnum.READ) - .cmHandleProperties(cmHandlePropertiesAsMap) - .build(); - return prepareOperationBody(requestBodyObject); - } - private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException { - final PersistenceCmHandlesList updatedPersistenceCmHandlesList = toPersistenceCmHandlesList( - dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getUpdatedCmHandles()); + final PersistenceCmHandlesList updatedPersistenceCmHandlesList = + getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getUpdatedCmHandles()); final String cmHandlesAsJson = objectMapper.writeValueAsString(updatedPersistenceCmHandlesList); cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", cmHandlesAsJson, NO_TIMESTAMP); } - private void parseAndCreateCmHandlesInDmiRegistrationAndSyncModule( - final DmiPluginRegistration dmiPluginRegistration) throws JsonProcessingException { - final PersistenceCmHandlesList createdPersistenceCmHandlesList = toPersistenceCmHandlesList( + private PersistenceCmHandlesList getUpdatedPersistenceCmHandlesList( + final DmiPluginRegistration dmiPluginRegistration, + final List<CmHandle> updatedCmHandles) { + return PersistenceCmHandlesList.toPersistenceCmHandlesList( dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getCreatedCmHandles()); - registerAndSyncNewCmHandles(createdPersistenceCmHandlesList); - } - - private static PersistenceCmHandlesList toPersistenceCmHandlesList(final String dmiPlugin, - final Collection<CmHandle> cmHandles) { - final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList(); - for (final CmHandle cmHandle : cmHandles) { - final PersistenceCmHandle persistenceCmHandle = toPersistenceCmHandle(dmiPlugin, cmHandle); - persistenceCmHandlesList.add(persistenceCmHandle); - } - return persistenceCmHandlesList; + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + updatedCmHandles); } private static void handleJsonProcessingException(final DmiPluginRegistration dmiPluginRegistration, @@ -403,19 +293,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService createAnchor(persistenceCmHandle); } - private static PersistenceCmHandle toPersistenceCmHandle(final String dmiPluginService, - final CmHandle cmHandle) { - final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle(); - persistenceCmHandle.setDmiServiceName(dmiPluginService); - persistenceCmHandle.setId(cmHandle.getCmHandleID()); - if (cmHandle.getCmHandleProperties() == null) { - persistenceCmHandle.setAdditionalProperties(Collections.emptyMap()); - } else { - persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties()); - } - return persistenceCmHandle; - } - private void parseAndRemoveCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) { for (final String cmHandle : dmiPluginRegistration.getRemovedCmHandles()) { try { @@ -428,11 +305,9 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } private void fetchAndSyncModules(final PersistenceCmHandle persistenceCmHandle) { - final Map<String, String> cmHandlePropertiesAsMap = getCmHandlePropertiesAsMap( - persistenceCmHandle.getAdditionalProperties()); final List<ModuleReference> moduleReferencesFromCmHandle = - fetchModuleReferencesFromDmi(persistenceCmHandle, cmHandlePropertiesAsMap); + toModuleReferences(dmiModelOperations.getModuleReferences(persistenceCmHandle)); final List<ModuleReference> existingModuleReferences = new ArrayList<>(); final List<ModuleReference> unknownModuleReferences = new ArrayList<>(); prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences); @@ -442,7 +317,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService newYangResourcesModuleNameToContentMap = new HashMap<>(); } else { newYangResourcesModuleNameToContentMap = getNewYangResourcesFromDmi(persistenceCmHandle, - unknownModuleReferences, cmHandlePropertiesAsMap); + unknownModuleReferences); } cpsModuleService .createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(), @@ -465,60 +340,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private List<ModuleReference> fetchModuleReferencesFromDmi(final PersistenceCmHandle persistenceCmHandle, - final Map<String, String> cmHandlePropertiesAsMap) { - final GenericRequestBody genericRequestBody = GenericRequestBody.builder() - .cmHandleProperties(cmHandlePropertiesAsMap) - .build(); - final String jsonBodyWithOnlyCmHandleProperties = prepareOperationBody(genericRequestBody); - final ResponseEntity<String> dmiFetchModulesResponseEntity = - dmiOperations.getResourceFromDmiWithJsonData(persistenceCmHandle.getDmiServiceName(), - jsonBodyWithOnlyCmHandleProperties, persistenceCmHandle.getId(), "modules"); - return toModuleReferences(dmiFetchModulesResponseEntity); - } - private void createAnchor(final PersistenceCmHandle persistenceCmHandle) { cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(), persistenceCmHandle.getId()); } - private String getRequestBodyToFetchYangResourceFromDmi(final List<ModuleReference> unknownModuleReferences, - final Map<String, String> cmHandlePropertiesAsMap) { - final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences); - final JsonObject data = new JsonObject(); - data.add("modules", moduleReferencesAsJson); - final JsonObject jsonRequestObject = new JsonObject(); - jsonRequestObject.add("data", data); - final Gson gson = new Gson(); - jsonRequestObject.add("cmHandleProperties", gson.toJsonTree(cmHandlePropertiesAsMap)); - return jsonRequestObject.toString(); - } - - private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) { - final JsonArray moduleReferences = new JsonArray(); - - for (final ModuleReference moduleReference : unknownModuleReferences) { - final JsonObject moduleReferenceAsJson = new JsonObject(); - moduleReferenceAsJson.addProperty("name", moduleReference.getModuleName()); - moduleReferenceAsJson.addProperty(REVISION, moduleReference.getRevision()); - moduleReferences.add(moduleReferenceAsJson); - } - return moduleReferences; - } - private Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle, - final List<ModuleReference> unknownModuleReferences, - final Map<String, String> cmHandlePropertiesAsMap) { - final String jsonDataWithDataAndCmHandleProperties = getRequestBodyToFetchYangResourceFromDmi( - unknownModuleReferences, cmHandlePropertiesAsMap); - - final ResponseEntity<String> moduleResourcesAsJsonString = dmiOperations.getResourceFromDmiWithJsonData( - persistenceCmHandle.getDmiServiceName(), - jsonDataWithDataAndCmHandleProperties, - persistenceCmHandle.getId(), - "moduleResources"); - - final JsonArray moduleResources = new Gson().fromJson(moduleResourcesAsJsonString.getBody(), + final List<ModuleReference> unknownModuleReferences) { + final ResponseEntity<String> responseEntity = + dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, unknownModuleReferences); + + final JsonArray moduleResources = new Gson().fromJson(responseEntity.getBody(), JsonArray.class); final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>(); @@ -532,7 +364,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static YangResource toYangResource(final JsonObject yangResourceAsJson) { final YangResource yangResource = new YangResource(); yangResource.setModuleName(yangResourceAsJson.get("moduleName").getAsString()); - yangResource.setRevision(yangResourceAsJson.get(REVISION).getAsString()); + yangResource.setRevision(yangResourceAsJson.get("revision").getAsString()); final String yangSourceJson = yangResourceAsJson.get("yangSource").getAsString(); String yangSource = JsonUtils.removeWrappingTokens(yangSourceJson); @@ -558,7 +390,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) { final ModuleReference moduleReference = new ModuleReference(); moduleReference.setModuleName(moduleReferenceAsJson.get("moduleName").getAsString()); - moduleReference.setRevision(moduleReferenceAsJson.get(REVISION).getAsString()); + moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString()); return moduleReference; } } 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 fc70708f9..62eca9451 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 @@ -35,13 +35,28 @@ public class DmiRestClient { private RestTemplate restTemplate; private DmiProperties dmiProperties; + /** + * Constructor injection for DmiRestClient objects. + * + * @param restTemplate the rest template + * @param dmiProperties the DMI properties + */ public DmiRestClient(final RestTemplate restTemplate, final DmiProperties dmiProperties) { this.restTemplate = restTemplate; this.dmiProperties = dmiProperties; } + /** + * Sends a PUT operation to DMI with JSON payload. + * + * @param dmiResourceUrl the DMI resource URL + * @param jsonData the JSON payload + * @param headers the HTTP headers + * @return response entity of type Object + */ public ResponseEntity<Object> putOperationWithJsonData(final String dmiResourceUrl, final String jsonData, final HttpHeaders headers) { + //TODO Toine Siebelink, should we use POST operation below instead (and return a String-Entity!) final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(headers)); return restTemplate.exchange(dmiResourceUrl, HttpMethod.PUT, httpEntity, Object.class); } @@ -76,4 +91,4 @@ public class DmiRestClient { final var httpEntity = new HttpEntity<>(configureHttpHeaders(httpHeaders)); return restTemplate.exchange(dmiResourceUrl, HttpMethod.POST, httpEntity, String.class); } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java index c4e82d329..81c9dff40 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java @@ -38,6 +38,8 @@ public class NcmpConfiguration { private String authUsername; @Value("${dmi.auth.password}") private String authPassword; + @Value("${dmi.api.base-path}") + private String dmiBasePath; } @Bean diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java deleted file mode 100644 index 40a47ecf6..000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 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.api.impl.operation; - -import com.fasterxml.jackson.annotation.JsonValue; -import lombok.Getter; -import org.onap.cps.ncmp.api.impl.client.DmiRestClient; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -@Component -public class DmiOperations { - @Getter - public enum DataStoreEnum { - PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), - PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); - private String value; - - DataStoreEnum(final String value) { - this.value = value; - } - - @Override - @JsonValue - public String toString() { - return value; - } - } - - private DmiRestClient dmiRestClient; - private static final String DMI_API_PATH = "/dmi"; - private static final String DMI_CM_HANDLE_PATH = "/v1/ch/{cmHandle}"; - private static final String DMI_CM_HANDLE_DATASTORE_PATH = DMI_CM_HANDLE_PATH + "/data/ds"; - private static final String URL_SEPARATOR = "/"; - private static final String RESOURCE_IDENTIFIER = "resourceIdentifier"; - private static final String OPTIONS_QUERY_KEY = "options"; - - - /** - * Constructor for {@code DmiOperations}. This method also manipulates url properties. - * - * @param dmiRestClient {@code DmiRestClient} - */ - public DmiOperations(final DmiRestClient dmiRestClient) { - this.dmiRestClient = dmiRestClient; - } - - /** - * Get resources from DMI. - * - * @param dmiServiceName dmi service name - * @param cmHandle cmHandle - * @param resourceName name of the resource(s) - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<String> getResourceFromDmi(final String dmiServiceName, - final String cmHandle, - final String resourceName) { - final var dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); - final var httpHeaders = new HttpHeaders(); - return dmiRestClient.postOperation(dmiResourceDataUrl, httpHeaders); - } - - /** - * Get resources from DMI for modules. - * - * @param dmiServiceName dmi service name - * @param jsonData module names and revisions as JSON - * @param cmHandle cmHandle - * @param resourceName name of the resource(s) - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<String> getResourceFromDmiWithJsonData(final String dmiServiceName, - final String jsonData, - final String cmHandle, - final String resourceName) { - final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders()); - } - - /** - * This method fetches the resource data from operational data store for given cm handle - * identifier on given resource using dmi client. - * - * @param dmiServiceName dmi service name - * @param cmHandle network resource identifier - * @param resourceId resource identifier - * @param optionsParamInQuery options query - * @param acceptParamInHeader accept parameter - * @param jsonBody json body for put operation - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<Object> getResourceDataOperationalFromDmi(final String dmiServiceName, - final String cmHandle, - final String resourceId, - final String optionsParamInQuery, - final String acceptParamInHeader, - final String jsonBody) { - final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId, - optionsParamInQuery, DataStoreEnum.PASSTHROUGH_OPERATIONAL); - final var httpHeaders = prepareHeader(acceptParamInHeader); - return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders); - } - - /** - * This method fetches the resource data from pass-through running data store for given cm handle - * identifier on given resource using dmi client. - * - * @param dmiServiceName dmi service name - * @param cmHandle network resource identifier - * @param resourceId resource identifier - * @param optionsParamInQuery fields query - * @param acceptParamInHeader accept parameter - * @param jsonBody json body for put operation - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<Object> getResourceDataPassThroughRunningFromDmi(final String dmiServiceName, - final String cmHandle, - final String resourceId, - final String optionsParamInQuery, - final String acceptParamInHeader, - final String jsonBody) { - final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId, - optionsParamInQuery, DataStoreEnum.PASSTHROUGH_RUNNING); - final var httpHeaders = prepareHeader(acceptParamInHeader); - return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders); - } - - /** - * This method creates the resource data from pass-through running data store for given cm handle - * identifier on given resource using dmi client. - * - * @param dmiServiceName dmi service name - * @param cmHandle network resource identifier - * @param resourceId resource identifier - * @param jsonBody json body for put operation - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<String> createResourceDataPassThroughRunningFromDmi(final String dmiServiceName, - final String cmHandle, - final String resourceId, - final String jsonBody) { - final var stringBuilder = getStringBuilderForPassThroughUrl(dmiServiceName, - cmHandle, resourceId, DataStoreEnum.PASSTHROUGH_RUNNING); - return dmiRestClient.postOperationWithJsonData(stringBuilder.toString(), jsonBody, new HttpHeaders()); - } - - private String getDmiResourceUrl(final String dmiServiceName, - final String cmHandle, - final String resourceName) { - final var stringBuilder = new StringBuilder(dmiServiceName); - stringBuilder.append(DMI_API_PATH); - stringBuilder.append(DMI_CM_HANDLE_PATH.replace("{cmHandle}", cmHandle)); - stringBuilder.append(URL_SEPARATOR + resourceName); - return stringBuilder.toString(); - } - - /** - * This method updates the resource data from pass-through running data store for the cm handle identifier on given - * resource using dmi client. - * - * @param dmiServiceName dmi service name - * @param cmHandle network resource identifier - * @param resourceId resource identifier - * @param jsonBody json body for put operation - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<String> updateResourceDataPassThroughRunningFromDmi(final String dmiServiceName, - final String cmHandle, final String resourceId, final String jsonBody) { - final StringBuilder stringBuilder = - getStringBuilderForPassThroughUrl(dmiServiceName, cmHandle, resourceId, DataStoreEnum.PASSTHROUGH_RUNNING); - return dmiRestClient.postOperationWithJsonData(stringBuilder.toString(), jsonBody, new HttpHeaders()); - } - - private String getDmiDatastoreUrl(final String dmiServiceName, - final String cmHandle, - final String resourceId, - final String optionsParamInQuery, - final DataStoreEnum dataStoreEnum) { - final var stringBuilder = getStringBuilderForPassThroughUrl(dmiServiceName, - cmHandle, resourceId, dataStoreEnum); - appendOptionsQuery(stringBuilder, optionsParamInQuery); - return stringBuilder.toString(); - } - - private StringBuilder getStringBuilderForPassThroughUrl(final String dmiServiceName, - final String cmHandle, - final String resourceId, - final DataStoreEnum dataStoreEnum) { - final var stringBuilder = new StringBuilder(dmiServiceName); - stringBuilder.append(DMI_API_PATH); - stringBuilder.append(DMI_CM_HANDLE_DATASTORE_PATH.replace("{cmHandle}", cmHandle)); - stringBuilder.append(URL_SEPARATOR + dataStoreEnum.getValue()); - stringBuilder.append("?" + RESOURCE_IDENTIFIER + "=" + resourceId); - return stringBuilder; - } - - private void appendOptionsQuery(final StringBuilder stringBuilder, - final String optionsParamInQuery) { - if (optionsParamInQuery != null) { - stringBuilder.append("&").append(OPTIONS_QUERY_KEY).append("=").append(optionsParamInQuery); - } - } - - private HttpHeaders prepareHeader(final String acceptParam) { - final var httpHeaders = new HttpHeaders(); - httpHeaders.set(HttpHeaders.ACCEPT, acceptParam); - return httpHeaders; - } -} 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 new file mode 100644 index 000000000..b8336455c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -0,0 +1,140 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.api.impl.operations; + +import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING; +import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; +import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ; +import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.onap.cps.ncmp.api.impl.client.DmiRestClient; +import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; +import org.onap.cps.ncmp.api.models.PersistenceCmHandle; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +/** + * Operations class for DMI data. + */ +@Component +public class DmiDataOperations extends DmiOperations { + + /** + * Constructor for {@code DmiOperations}. This method also manipulates url properties. + * + * @param dmiRestClient {@code DmiRestClient} + */ + public DmiDataOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, + final ObjectMapper objectMapper, + final NcmpConfiguration.DmiProperties dmiProperties, + final DmiRestClient dmiRestClient) { + super(cmHandlePropertiesRetriever, objectMapper, dmiProperties, dmiRestClient); + } + + /** + * This method fetches the resource data from operational data store for given cm handle + * identifier on given resource using dmi client. + * + * @param cmHandle network resource identifier + * @param resourceId resource identifier + * @param optionsParamInQuery options query + * @param acceptParamInHeader accept parameter + * @param dataStore data store enum + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandle, + final String resourceId, + final String optionsParamInQuery, + final String acceptParamInHeader, + final DataStoreEnum dataStore) { + final PersistenceCmHandle persistenceCmHandle = + cmHandlePropertiesRetriever.retrieveCmHandleDmiServiceNameAndProperties(cmHandle); + final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() + .operation(READ) + .build(); + dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); + final String jsonBody = getDmiRequestBodyAsString(dmiRequestBody); + + final var dmiResourceDataUrl = getDmiDatastoreUrlWithOptions( + persistenceCmHandle.resolveDmiServiceName(DATA), cmHandle, resourceId, + optionsParamInQuery, dataStore); + final var httpHeaders = prepareHeader(acceptParamInHeader); + return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders); + } + + /** + * This method creates the resource data from pass-through running data store for given cm handle + * identifier on given resource using dmi client. + * + * @param cmHandle network resource identifier + * @param resourceId resource identifier + * @param operation operation enum + * @param requestData the request data + * @param dataType data type + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<String> writeResourceDataPassThroughRunningFromDmi(final String cmHandle, + final String resourceId, + final OperationEnum operation, + final String requestData, + final String dataType) { + final PersistenceCmHandle persistenceCmHandle = + cmHandlePropertiesRetriever.retrieveCmHandleDmiServiceNameAndProperties(cmHandle); + final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() + .operation(operation) + .data(requestData) + .dataType(dataType) + .build(); + dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); + final String jsonBody = getDmiRequestBodyAsString(dmiRequestBody); + final String dmiUrl = + getResourceInDataStoreUrl(persistenceCmHandle.resolveDmiServiceName(DATA), + cmHandle, resourceId, PASSTHROUGH_RUNNING); + return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, new HttpHeaders()); + } + + private String getResourceInDataStoreUrl(final String dmiServiceName, + final String cmHandle, + final String resourceId, + final DataStoreEnum dataStoreEnum) { + return getCmHandleUrl(dmiServiceName, cmHandle) + + "data" + + URL_SEPARATOR + + "ds" + + URL_SEPARATOR + + dataStoreEnum.getValue() + + "?resourceIdentifier=" + + resourceId; + } + + private String getDmiDatastoreUrlWithOptions(final String dmiServiceName, + final String cmHandle, + final String resourceId, + final String optionsParamInQuery, + final DataStoreEnum dataStoreEnum) { + final String resourceInDataStoreUrl = getResourceInDataStoreUrl(dmiServiceName, + cmHandle, resourceId, dataStoreEnum); + return appendOptionsQuery(resourceInDataStoreUrl, optionsParamInQuery); + } + +} 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 new file mode 100644 index 000000000..c58258466 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -0,0 +1,133 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.api.impl.operations; + +import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.List; +import org.onap.cps.ncmp.api.impl.client.DmiRestClient; +import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; +import org.onap.cps.ncmp.api.models.PersistenceCmHandle; +import org.onap.cps.spi.model.ModuleReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component +public class DmiModelOperations extends DmiOperations { + + /** + * Constructor for {@code DmiOperations}. This method also manipulates url properties. + * + * @param dmiRestClient {@code DmiRestClient} + */ + public DmiModelOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, + final ObjectMapper objectMapper, + final NcmpConfiguration.DmiProperties dmiProperties, + final DmiRestClient dmiRestClient) { + super(cmHandlePropertiesRetriever, objectMapper, dmiProperties, dmiRestClient); + } + + /** + * Retrieves module references. + * + * @param persistenceCmHandle the persistence cm handle + * @return module references + */ + public ResponseEntity<String> getModuleReferences(final PersistenceCmHandle persistenceCmHandle) { + final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() + .build(); + dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); + return getResourceFromDmiWithJsonData(persistenceCmHandle.resolveDmiServiceName(MODEL), + getDmiRequestBodyAsString(dmiRequestBody), persistenceCmHandle.getId(), "modules"); + } + + /** + * Retrieve yang resources from dmi for any modules that CPS-NCMP hasn't cached before. + * + * @param persistenceCmHandle the persistenceCmHandle + * @param unknownModuleReferences the unknown module references + * @return yang resources + */ + public ResponseEntity<String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle, + final List<ModuleReference> unknownModuleReferences) { + final String jsonDataWithDataAndCmHandleProperties = getRequestBodyToFetchYangResources( + unknownModuleReferences, persistenceCmHandle.getAdditionalProperties()); + return getResourceFromDmiWithJsonData( + persistenceCmHandle.resolveDmiServiceName(MODEL), + jsonDataWithDataAndCmHandleProperties, + persistenceCmHandle.getId(), + "moduleResources"); + } + + /** + * Get resources from DMI for modules. + * + * @param dmiServiceName dmi service name + * @param jsonData module names and revisions as JSON + * @param cmHandle cmHandle + * @param resourceName name of the resource(s) + * @return {@code ResponseEntity} response entity + */ + private ResponseEntity<String> getResourceFromDmiWithJsonData(final String dmiServiceName, + final String jsonData, + final String cmHandle, + final String resourceName) { + final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders()); + } + + private static String getRequestBodyToFetchYangResources(final List<ModuleReference> unknownModuleReferences, + final List<PersistenceCmHandle.AdditionalProperty> cmHandleProperties) { + final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences); + final JsonObject data = new JsonObject(); + data.add("modules", moduleReferencesAsJson); + final JsonObject jsonRequestObject = new JsonObject(); + jsonRequestObject.add("data", data); + jsonRequestObject.add("cmHandleProperties", toJsonObject(cmHandleProperties)); + return jsonRequestObject.toString(); + } + + private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) { + final JsonArray moduleReferences = new JsonArray(); + + for (final ModuleReference moduleReference : unknownModuleReferences) { + final JsonObject moduleReferenceAsJson = new JsonObject(); + moduleReferenceAsJson.addProperty("name", moduleReference.getModuleName()); + moduleReferenceAsJson.addProperty("revision", moduleReference.getRevision()); + moduleReferences.add(moduleReferenceAsJson); + } + return moduleReferences; + } + + private static JsonObject toJsonObject(final List<PersistenceCmHandle.AdditionalProperty> cmHandleProperties) { + //TODO Toine/Joe Double check format with existing test data + final JsonObject asJsonObject = new JsonObject(); + for (final PersistenceCmHandle.AdditionalProperty additionalProperty : cmHandleProperties) { + asJsonObject.addProperty(additionalProperty.getName(), additionalProperty.getValue()); + } + return asJsonObject; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java new file mode 100644 index 000000000..509728061 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java @@ -0,0 +1,114 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.api.impl.operations; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.impl.client.DmiRestClient; +import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; +import org.onap.cps.ncmp.api.impl.exception.NcmpException; +import org.springframework.http.HttpHeaders; + +@Slf4j +public class DmiOperations { + + @Getter + public enum DataStoreEnum { + PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), + PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); + private String value; + + DataStoreEnum(final String value) { + this.value = value; + } + } + + protected ObjectMapper objectMapper; + protected PersistenceCmHandleRetriever cmHandlePropertiesRetriever; + protected DmiRestClient dmiRestClient; + protected NcmpConfiguration.DmiProperties dmiProperties; + + static final String URL_SEPARATOR = "/"; + + /** + * Constructor for {@code DmiOperations}. This method also manipulates url properties. + * + * @param dmiRestClient {@code DmiRestClient} + */ + public DmiOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, + final ObjectMapper objectMapper, + final NcmpConfiguration.DmiProperties dmiProperties, + final DmiRestClient dmiRestClient) { + this.cmHandlePropertiesRetriever = cmHandlePropertiesRetriever; + this.objectMapper = objectMapper; + this.dmiRestClient = dmiRestClient; + this.dmiProperties = dmiProperties; + } + + String getCmHandleUrl(final String dmiServiceName, final String cmHandle) { + return dmiServiceName + + dmiProperties.getDmiBasePath() + + URL_SEPARATOR + + "v1" + + URL_SEPARATOR + + "ch" + + URL_SEPARATOR + + cmHandle + + URL_SEPARATOR; + } + + String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) { + return getCmHandleUrl(dmiServiceName, cmHandle) + resourceName; + } + + static String appendOptionsQuery(final String url, final String optionsParamInQuery) { + if (Strings.isNullOrEmpty(optionsParamInQuery)) { + return url; + } + return url + "&options=" + optionsParamInQuery; + } + + static HttpHeaders prepareHeader(final String acceptParam) { + final var httpHeaders = new HttpHeaders(); + httpHeaders.set(HttpHeaders.ACCEPT, acceptParam); + return httpHeaders; + } + + /** + * Convert DmiRequestBody to JSON. + * + * @param dmiRequestBody the dmi request body + * @return DmiRequestBody as JSON + */ + String getDmiRequestBodyAsString(final DmiRequestBody dmiRequestBody) { + try { + return objectMapper.writeValueAsString(dmiRequestBody); + } catch (final JsonProcessingException e) { + log.error("Parsing error occurred while converting Object to JSON."); + throw new NcmpException("Parsing error occurred while converting given object to JSON.", + e.getMessage()); + } + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/GenericRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java index 3e1ba4a97..13cfdcefe 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/GenericRequestBody.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java @@ -18,19 +18,22 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.impl.operations; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import lombok.Builder; import lombok.Getter; +import org.onap.cps.ncmp.api.models.PersistenceCmHandle; -@JsonInclude(Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) @Getter @Builder -public class GenericRequestBody { +public class DmiRequestBody { public enum OperationEnum { READ("read"), CREATE("create"), @@ -52,4 +55,25 @@ public class GenericRequestBody { private String dataType; private String data; private Map<String, String> cmHandleProperties; + + /** + * Set CmHandleProperties by converting a list of PersistenceCmHandle.AdditionalProperty objects. + * + * @param cmHandlePropertiesAsList the cm handle additional properties + */ + public void asCmHandleProperties( + final List<PersistenceCmHandle.AdditionalProperty> cmHandlePropertiesAsList) { + final boolean isCmHandlePropertiesNullOrEmpty = + cmHandlePropertiesAsList == null || cmHandlePropertiesAsList.isEmpty(); + if (isCmHandlePropertiesNullOrEmpty) { + cmHandleProperties = Collections.emptyMap(); + } else { + cmHandleProperties = new LinkedHashMap<>(); + for (final PersistenceCmHandle.AdditionalProperty additionalProperty : cmHandlePropertiesAsList) { + cmHandleProperties.put(additionalProperty.getName(), + additionalProperty.getValue()); + } + } + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/PersistenceCmHandleRetriever.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/PersistenceCmHandleRetriever.java new file mode 100644 index 000000000..78b5c318d --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/PersistenceCmHandleRetriever.java @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.api.impl.operations; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.models.CmHandle; +import org.onap.cps.ncmp.api.models.PersistenceCmHandle; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Component; + +/** + * Retrieves PersistenceCmHandles & properties. + */ +@Component +public class PersistenceCmHandleRetriever { + + private static final String NCMP_DATASPACE_NAME = "NCMP-Admin"; + private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry"; + + private CpsDataService cpsDataService; + + /** + * Constructor for PersistenceCmHandleRetriever. + * + * @param cpsDataService the cps data service. + */ + public PersistenceCmHandleRetriever(final CpsDataService cpsDataService) { + this.cpsDataService = cpsDataService; + } + + /** + * This method retieves dmi service name and properties for a given cm handle. + * @param cmHandleId the id of the cm handle + * @return persistence cm handle + */ + public PersistenceCmHandle retrieveCmHandleDmiServiceNameAndProperties(final String cmHandleId) { + final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId); + final CmHandle cmHandle = new CmHandle(cmHandleId, getCmHandleProperties(cmHandleDataNode)); + return PersistenceCmHandle.toPersistenceCmHandle( + String.valueOf(cmHandleDataNode.getLeaves().get("dmi-service-name")), + String.valueOf(cmHandleDataNode.getLeaves().get("dmi-data-service-name")), + String.valueOf(cmHandleDataNode.getLeaves().get("dmi-model-service-name")), + cmHandle + ); + } + + private DataNode getCmHandleDataNode(final String cmHandle) { + final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']"; + return cpsDataService.getDataNode(NCMP_DATASPACE_NAME, + NCMP_DMI_REGISTRY_ANCHOR, + xpathForDmiRegistryToFetchCmHandle, + FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + } + + private static Map<String, String> getCmHandleProperties(final DataNode cmHandleDataNode) { + if (cmHandleDataNode.getChildDataNodes().isEmpty()) { + return Collections.emptyMap(); + } + final Map<String, String> cmHandlePropertiesAsMap = new LinkedHashMap<>(); + for (final DataNode childDataNode: cmHandleDataNode.getChildDataNodes()) { + cmHandlePropertiesAsMap.put(String.valueOf(childDataNode.getLeaves().get("name")), + String.valueOf(childDataNode.getLeaves().get("value"))); + } + return cmHandlePropertiesAsMap; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/RequiredDmiService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/RequiredDmiService.java new file mode 100644 index 000000000..7e39766f3 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/RequiredDmiService.java @@ -0,0 +1,28 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.api.impl.operations; + +/** + * Enmm to determine if the required service is for a data or model operation. + */ +public enum RequiredDmiService { + DATA, MODEL +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java index 84227ed6b..a38442b09 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandle.java @@ -22,7 +22,9 @@ package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.springframework.validation.annotation.Validated; @@ -32,6 +34,8 @@ import org.springframework.validation.annotation.Validated; @Validated @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class CmHandle { @JsonProperty("cmHandle") diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java index f5a0d7954..a604f3448 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java @@ -18,14 +18,17 @@ * ============LICENSE_END========================================================= */ - package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.google.common.base.Strings; import java.util.List; import lombok.Getter; import lombok.Setter; +import org.onap.cps.ncmp.api.impl.exception.NcmpException; /** * Dmi Registry request object. @@ -35,12 +38,53 @@ import lombok.Setter; @JsonInclude(Include.NON_NULL) public class DmiPluginRegistration { + @JsonSetter(nulls = Nulls.AS_EMPTY) private String dmiPlugin; + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String dmiDataPlugin; + + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String dmiModelPlugin; + private List<CmHandle> createdCmHandles; private List<CmHandle> updatedCmHandles; private List<String> removedCmHandles; + public static final String PLEASE_SUPPLY_CORRECT_PLUGIN_INFORMATION = "Please supply correct plugin information."; + + /** + * Validates plugin service names. + * + * @throws NcmpException if validation fails. + */ + public void validateDmiPluginRegistration() throws NcmpException { + final String combinedServiceName = dmiPlugin; + final String dataServiceName = dmiDataPlugin; + final String modelsServiceName = dmiModelPlugin; + + String errorMessage = null; + + if (isNullEmptyOrBlank(combinedServiceName) + && isNullEmptyOrBlank(dataServiceName) + && isNullEmptyOrBlank(modelsServiceName)) { + errorMessage = "No DMI plugin service names"; + } + + if (!isNullEmptyOrBlank(combinedServiceName) + && (!isNullEmptyOrBlank(dataServiceName) || !isNullEmptyOrBlank(modelsServiceName))) { + errorMessage = "Invalid combination of plugin service names"; + } + + if (errorMessage != null) { + throw new NcmpException(errorMessage, PLEASE_SUPPLY_CORRECT_PLUGIN_INFORMATION); + } + } + + private static boolean isNullEmptyOrBlank(final String serviceName) { + return Strings.isNullOrEmpty(serviceName) || serviceName.isBlank(); + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java index 88d97d25f..8b959e327 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandle.java @@ -22,13 +22,17 @@ package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Strings; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; +import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; /** * DmiRegistry. @@ -43,22 +47,72 @@ public class PersistenceCmHandle { @JsonProperty("dmi-service-name") private String dmiServiceName; + @JsonProperty("dmi-data-service-name") + private String dmiDataServiceName; + + @JsonProperty("dmi-model-service-name") + private String dmiModelServiceName; + @JsonProperty("additional-properties") private List<AdditionalProperty> additionalProperties; /** + * Create a persistenceCmHandle. + * @param dmiServiceName dmi service name + * @param dmiDataServiceName dmi data service name + * @param dmiModelServiceName dmi model service name + * @param cmHandle the cm handle + * @return instance of persistenceCmHandle + */ + public static PersistenceCmHandle toPersistenceCmHandle(final String dmiServiceName, + final String dmiDataServiceName, + final String dmiModelServiceName, + final CmHandle cmHandle) { + final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle(); + persistenceCmHandle.setId(cmHandle.getCmHandleID()); + persistenceCmHandle.setDmiServiceName(dmiServiceName); + persistenceCmHandle.setDmiDataServiceName(dmiDataServiceName); + persistenceCmHandle.setDmiModelServiceName(dmiModelServiceName); + if (cmHandle.getCmHandleProperties() == null) { + persistenceCmHandle.asAdditionalProperties(Collections.emptyMap()); + } else { + persistenceCmHandle.asAdditionalProperties(cmHandle.getCmHandleProperties()); + } + return persistenceCmHandle; + } + + /** * Set Additional Properties map, key and value pair. * @param additionalPropertiesAsMap Map of Additional Properties */ - public void setAdditionalProperties(final Map<String, String> additionalPropertiesAsMap) { + public void asAdditionalProperties(final Map<String, String> additionalPropertiesAsMap) { additionalProperties = new ArrayList<>(additionalPropertiesAsMap.size()); for (final Map.Entry<String, String> entry : additionalPropertiesAsMap.entrySet()) { additionalProperties.add(new AdditionalProperty(entry.getKey(), entry.getValue())); } } + /** + * Resolve a dmi service name. + * @param requiredService indicates what typo of service is required + * @return dmi service name + */ + public String resolveDmiServiceName(final RequiredDmiService requiredService) { + if (isNullEmptyOrBlank(dmiServiceName)) { + if (RequiredDmiService.DATA.equals(requiredService)) { + return dmiDataServiceName; + } + return dmiModelServiceName; + } + return dmiServiceName; + } + + private static boolean isNullEmptyOrBlank(final String serviceName) { + return Strings.isNullOrEmpty(serviceName) || serviceName.isBlank(); + } + @AllArgsConstructor - @Getter + @Data public static class AdditionalProperty { @JsonProperty() diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java index f35abf60b..d4f6e952d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import lombok.Getter; @@ -32,6 +33,31 @@ public class PersistenceCmHandlesList { private List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>(); /** + * Create a PersistenceCmHandleList given all service names and a collection of cmHandles. + * @param dmiServiceName the dmi service name + * @param dmiDataServiceName the dmi data service name + * @param dmiModelServiceName the dmi model service name + * @param cmHandles cm handles + * @return instance of PersistenceCmHandleList + */ + public static PersistenceCmHandlesList toPersistenceCmHandlesList(final String dmiServiceName, + final String dmiDataServiceName, + final String dmiModelServiceName, + final Collection<CmHandle> cmHandles) { + final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList(); + for (final CmHandle cmHandle : cmHandles) { + final PersistenceCmHandle persistenceCmHandle = + PersistenceCmHandle.toPersistenceCmHandle( + dmiServiceName, + dmiDataServiceName, + dmiModelServiceName, + cmHandle); + persistenceCmHandlesList.add(persistenceCmHandle); + } + return persistenceCmHandlesList; + } + + /** * Add a persistenceCmHandle. * * @param persistenceCmHandle the persistenceCmHandle to add |