diff options
author | Lee Anjella Macabuhay <lee.anjella.macabuhay@est.tech> | 2024-01-22 09:40:02 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2024-01-22 09:40:02 +0000 |
commit | b0e7e756cfbaa9828d503891b47c8ac75c2dc0d3 (patch) | |
tree | 6a962973ef5528cbef982a8b54122185dd4a06c7 | |
parent | 2ba1fea36de49e9b9e82882ead17fa51f53ea66f (diff) | |
parent | 84c8d8d383469e05420f8e3e482d00e527c87734 (diff) |
Merge "Remove Lambda & Restructure File"
3 files changed, 298 insertions, 309 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java index a31332f094..0ed95adff2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-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. @@ -164,12 +164,12 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState } private void setInitialStates(final YangModelCmHandle yangModelCmHandle) { - CompositeStateUtils.setInitialDataStoreSyncState().accept(yangModelCmHandle.getCompositeState()); - CompositeStateUtils.setCompositeState(READY).accept(yangModelCmHandle.getCompositeState()); + CompositeStateUtils.setInitialDataStoreSyncState(yangModelCmHandle.getCompositeState()); + CompositeStateUtils.setCompositeState(READY, yangModelCmHandle.getCompositeState()); } private void retryCmHandle(final YangModelCmHandle yangModelCmHandle) { - CompositeStateUtils.setCompositeStateForRetry().accept(yangModelCmHandle.getCompositeState()); + CompositeStateUtils.setCompositeStateForRetry(yangModelCmHandle.getCompositeState()); } private void registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) { @@ -178,7 +178,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState } private void setCmHandleState(final YangModelCmHandle yangModelCmHandle, final CmHandleState targetCmHandleState) { - CompositeStateUtils.setCompositeState(targetCmHandleState).accept(yangModelCmHandle.getCompositeState()); + CompositeStateUtils.setCompositeState(targetCmHandleState, yangModelCmHandle.getCompositeState()); } private boolean isNew(final CompositeState existingCompositeState) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java index 99cca8c0b3..35ad54fdef 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-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. @@ -20,7 +20,6 @@ package org.onap.cps.ncmp.api.impl.inventory; -import java.util.function.Consumer; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,31 +33,23 @@ public class CompositeStateUtils { /** * Sets the cmHandleState to the provided state and updates the timestamp. - * - * @return Updated CompositeState */ - public static Consumer<CompositeState> setCompositeState(final CmHandleState cmHandleState) { - return compositeState -> { - compositeState.setCmHandleState(cmHandleState); - compositeState.setLastUpdateTimeNow(); - }; + public static void setCompositeState(final CmHandleState cmHandleState, + final CompositeState compositeState) { + compositeState.setCmHandleState(cmHandleState); + compositeState.setLastUpdateTimeNow(); } /** * Set the Operational datastore sync state based on the global flag. - * - * @return Updated CompositeState */ - public static Consumer<CompositeState> setInitialDataStoreSyncState() { - - return compositeState -> { - compositeState.setDataSyncEnabled(false); - final CompositeState.Operational operational = - getInitialDataStoreSyncState(compositeState.getDataSyncEnabled()); - final CompositeState.DataStores dataStores = - CompositeState.DataStores.builder().operationalDataStore(operational).build(); - compositeState.setDataStores(dataStores); - }; + public static void setInitialDataStoreSyncState(final CompositeState compositeState) { + compositeState.setDataSyncEnabled(false); + final CompositeState.Operational operational = + getInitialDataStoreSyncState(compositeState.getDataSyncEnabled()); + final CompositeState.DataStores dataStores = + CompositeState.DataStores.builder().operationalDataStore(operational).build(); + compositeState.setDataStores(dataStores); } /** @@ -91,19 +82,15 @@ public class CompositeStateUtils { /** * Sets the cmHandleState to ADVISED and retain the lock details. Used in retry scenarios. - * - * @return Updated CompositeState */ - public static Consumer<CompositeState> setCompositeStateForRetry() { - return compositeState -> { - compositeState.setCmHandleState(CmHandleState.ADVISED); - compositeState.setLastUpdateTimeNow(); - final String oldLockReasonDetails = compositeState.getLockReason().getDetails(); - final CompositeState.LockReason lockReason = - CompositeState.LockReason.builder() - .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory()) - .details(oldLockReasonDetails).build(); - compositeState.setLockReason(lockReason); - }; + public static void setCompositeStateForRetry(final CompositeState compositeState) { + compositeState.setCmHandleState(CmHandleState.ADVISED); + compositeState.setLastUpdateTimeNow(); + final String oldLockReasonDetails = compositeState.getLockReason().getDetails(); + final CompositeState.LockReason lockReason = + CompositeState.LockReason.builder() + .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory()) + .details(oldLockReasonDetails).build(); + compositeState.setLockReason(lockReason); } } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index 1cfe21d3a2..fd47793a7a 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -84,67 +84,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@.+?])?)"; @Override - public void addChildDataNodes(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final Collection<DataNode> dataNodes) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes); - } - - @Override - public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection<DataNode> newListElements) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements); - } - - private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath, - final DataNode newChild) { - final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); - final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - try { - fragmentRepository.save(newChildAsFragmentEntity); - } catch (final DataIntegrityViolationException dataIntegrityViolationException) { - throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()), - anchorEntity.getName()); - } - } - - private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath, - final Collection<DataNode> newChildren) { - final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); - final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size()); - try { - for (final DataNode newChildAsDataNode : newChildren) { - final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - fragmentEntities.add(newChildAsFragmentEntity); - } - fragmentRepository.saveAll(fragmentEntities); - } catch (final DataIntegrityViolationException dataIntegrityViolationException) { - log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", - dataIntegrityViolationException, fragmentEntities.size()); - retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren); - } - } - - private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath, - final Collection<DataNode> newChildren) { - final Collection<String> failedXpaths = new HashSet<>(); - for (final DataNode newChild : newChildren) { - try { - addNewChildDataNode(anchorEntity, parentNodeXpath, newChild); - } catch (final AlreadyDefinedException alreadyDefinedException) { - failedXpaths.add(newChild.getXpath()); - } - } - if (!failedXpaths.isEmpty()) { - throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName()); - } - } - - @Override public void storeDataNodes(final String dataspaceName, final String anchorName, final Collection<DataNode> dataNodes) { final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); @@ -157,7 +96,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService fragmentRepository.saveAll(fragmentEntities); } catch (final DataIntegrityViolationException exception) { log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually", - exception, dataNodes.size()); + exception, dataNodes.size()); storeDataNodesIndividually(anchorEntity, dataNodes); } } @@ -197,79 +136,153 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return parentFragment; } - private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) { - return FragmentEntity.builder() - .anchor(anchorEntity) - .xpath(dataNode.getXpath()) - .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) - .build(); + @Override + public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final Collection<DataNode> newListElements) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements); } @Override - @Timed(value = "cps.data.persistence.service.datanode.get", - description = "Time taken to get a data node") - public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName, - final String xpath, - final FetchDescendantsOption fetchDescendantsOption) { - final String targetXpath = getNormalizedXpath(xpath); - final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName, - Collections.singletonList(targetXpath), fetchDescendantsOption); - if (dataNodes.isEmpty()) { - throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath); + public void addChildDataNodes(final String dataspaceName, final String anchorName, + final String parentNodeXpath, final Collection<DataNode> dataNodes) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes); + } + + private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath, + final Collection<DataNode> newChildren) { + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); + final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size()); + try { + for (final DataNode newChildAsDataNode : newChildren) { + final FragmentEntity newChildAsFragmentEntity = + convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode); + newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); + fragmentEntities.add(newChildAsFragmentEntity); + } + fragmentRepository.saveAll(fragmentEntities); + } catch (final DataIntegrityViolationException dataIntegrityViolationException) { + log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", + dataIntegrityViolationException, fragmentEntities.size()); + retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren); } - return dataNodes; } - @Override - @Timed(value = "cps.data.persistence.service.datanode.batch.get", - description = "Time taken to get data nodes") - public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName, - final Collection<String> xpaths, - final FetchDescendantsOption fetchDescendantsOption) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths); - fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, - fragmentEntities); - return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); + private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath, + final DataNode newChild) { + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); + final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild); + newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); + try { + fragmentRepository.save(newChildAsFragmentEntity); + } catch (final DataIntegrityViolationException dataIntegrityViolationException) { + throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()), + anchorEntity.getName()); + } } - private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity, - final Collection<String> xpaths) { - final Collection<String> normalizedXpaths = getNormalizedXpaths(xpaths); + private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath, + final Collection<DataNode> newChildren) { + final Collection<String> failedXpaths = new HashSet<>(); + for (final DataNode newChild : newChildren) { + try { + addNewChildDataNode(anchorEntity, parentNodeXpath, newChild); + } catch (final AlreadyDefinedException alreadyDefinedException) { + failedXpaths.add(newChild.getXpath()); + } + } + if (!failedXpaths.isEmpty()) { + throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName()); + } + } - final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath); + @Override + public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName, + final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, - normalizedXpaths); + final Collection<String> xpathsOfUpdatedLeaves = updatedLeavesPerXPath.keySet(); + final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpathsOfUpdatedLeaves); for (final FragmentEntity fragmentEntity : fragmentEntities) { - normalizedXpaths.remove(fragmentEntity.getXpath()); + final Map<String, Serializable> updatedLeaves = updatedLeavesPerXPath.get(fragmentEntity.getXpath()); + final String mergedLeaves = mergeLeaves(updatedLeaves, fragmentEntity.getAttributes()); + fragmentEntity.setAttributes(mergedLeaves); } - for (final String xpath : normalizedXpaths) { - if (!CpsPathUtil.isPathToListElement(xpath)) { - fragmentEntities.addAll(fragmentRepository.findListByAnchorAndXpath(anchorEntity, xpath)); - } + try { + fragmentRepository.saveAll(fragmentEntities); + } catch (final StaleStateException staleStateException) { + retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities); } + } - if (haveRootXpath) { - fragmentEntities.addAll(fragmentRepository.findRootsByAnchorId(anchorEntity.getId())); + @Override + public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, + final Collection<DataNode> updatedDataNodes) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + + final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream() + .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); + + final Collection<String> xpaths = xpathToUpdatedDataNode.keySet(); + Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths); + existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities( + FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities); + + for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) { + final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath()); + updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode); } - return fragmentEntities; + try { + fragmentRepository.saveAll(existingFragmentEntities); + } catch (final StaleStateException staleStateException) { + retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities); + } } - private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) { - final FragmentEntity fragmentEntity; - if (isRootXpath(xpath)) { - fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null); - } else { - fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath)); + private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity, + final Collection<FragmentEntity> fragmentEntities) { + final Collection<String> failedXpaths = new HashSet<>(); + for (final FragmentEntity dataNodeFragment : fragmentEntities) { + try { + fragmentRepository.save(dataNodeFragment); + } catch (final StaleStateException staleStateException) { + failedXpaths.add(dataNodeFragment.getXpath()); + } } - if (fragmentEntity == null) { - throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath); + if (!failedXpaths.isEmpty()) { + final String failedXpathsConcatenated = String.join(",", failedXpaths); + throw new ConcurrencyException("Concurrent Transactions", String.format( + "DataNodes : %s in Dataspace :'%s' with Anchor : '%s' are updated by another transaction.", + failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName())); } - return fragmentEntity; + } + + private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity, + final DataNode newDataNode) { + copyAttributesFromNewDataNode(existingFragmentEntity, newDataNode); + + final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream() + .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); + + final Collection<FragmentEntity> updatedChildFragments = new HashSet<>(); + for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) { + final FragmentEntity childFragment; + if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) { + childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(), + newDataNodeChild); + } else { + childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath()); + updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild); + } + updatedChildFragments.add(childFragment); + } + + existingFragmentEntity.getChildFragments().clear(); + existingFragmentEntity.getChildFragments().addAll(updatedChildFragments); } @Override @@ -338,11 +351,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } - private List<Long> getAnchorIdsForPagination(final DataspaceEntity dataspaceEntity, final CpsPathQuery cpsPathQuery, - final PaginationOption paginationOption) { - return fragmentRepository.findAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, paginationOption); - } - private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption, final Collection<FragmentEntity> fragmentEntities) { final List<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size()); @@ -352,29 +360,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return Collections.unmodifiableList(dataNodes); } - private static String getNormalizedXpath(final String xpathSource) { - if (isRootXpath(xpathSource)) { - return xpathSource; - } - try { - return CpsPathUtil.getNormalizedXpath(xpathSource); - } catch (final PathParsingException pathParsingException) { - throw new CpsPathException(pathParsingException.getMessage()); - } - } - - private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) { - final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size()); - for (final String xpath : xpaths) { - try { - normalizedXpaths.add(getNormalizedXpath(xpath)); - } catch (final CpsPathException cpsPathException) { - log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage()); - } - } - return normalizedXpaths; - } - @Override public String startSession() { return sessionManager.startSession(); @@ -404,21 +389,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return anchorIdList.size(); } - private static Set<String> processAncestorXpath(final Collection<FragmentEntity> fragmentEntities, - final CpsPathQuery cpsPathQuery) { - final Set<String> ancestorXpath = new HashSet<>(); - final Pattern pattern = - Pattern.compile("(.*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier()) - + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/.*"); - for (final FragmentEntity fragmentEntity : fragmentEntities) { - final Matcher matcher = pattern.matcher(fragmentEntity.getXpath()); - if (matcher.matches()) { - ancestorXpath.add(matcher.group(1)); - } - } - return ancestorXpath; - } - private DataNode toDataNode(final FragmentEntity fragmentEntity, final FetchDescendantsOption fetchDescendantsOption) { final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); @@ -434,103 +404,15 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService .withChildDataNodes(childDataNodes).build(); } - private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, - final FetchDescendantsOption fetchDescendantsOption) { - if (fetchDescendantsOption.hasNext()) { - return fragmentEntity.getChildFragments().stream() - .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next())) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - @Override - public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName, - final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - - final Collection<String> xpathsOfUpdatedLeaves = updatedLeavesPerXPath.keySet(); - final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpathsOfUpdatedLeaves); - - for (final FragmentEntity fragmentEntity : fragmentEntities) { - final Map<String, Serializable> updatedLeaves = updatedLeavesPerXPath.get(fragmentEntity.getXpath()); - final String mergedLeaves = mergeLeaves(updatedLeaves, fragmentEntity.getAttributes()); - fragmentEntity.setAttributes(mergedLeaves); - } - - try { - fragmentRepository.saveAll(fragmentEntities); - } catch (final StaleStateException staleStateException) { - retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities); - } - } - - @Override - public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, - final Collection<DataNode> updatedDataNodes) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - - final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream() - .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); - - final Collection<String> xpaths = xpathToUpdatedDataNode.keySet(); - Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths); - existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities( - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities); - - for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) { - final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode); - } - - try { - fragmentRepository.saveAll(existingFragmentEntities); - } catch (final StaleStateException staleStateException) { - retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities); - } - } - - private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity, - final Collection<FragmentEntity> fragmentEntities) { - final Collection<String> failedXpaths = new HashSet<>(); - for (final FragmentEntity dataNodeFragment : fragmentEntities) { - try { - fragmentRepository.save(dataNodeFragment); - } catch (final StaleStateException staleStateException) { - failedXpaths.add(dataNodeFragment.getXpath()); - } - } - if (!failedXpaths.isEmpty()) { - final String failedXpathsConcatenated = String.join(",", failedXpaths); - throw new ConcurrencyException("Concurrent Transactions", String.format( - "DataNodes : %s in Dataspace :'%s' with Anchor : '%s' are updated by another transaction.", - failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName())); - } + private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) { + return FragmentEntity.builder() + .anchor(anchorEntity) + .xpath(dataNode.getXpath()) + .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) + .build(); } - private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity, - final DataNode newDataNode) { - copyAttributesFromNewDataNode(existingFragmentEntity, newDataNode); - - final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream() - .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); - - final Collection<FragmentEntity> updatedChildFragments = new HashSet<>(); - for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) { - final FragmentEntity childFragment; - if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) { - childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(), - newDataNodeChild); - } else { - childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild); - } - updatedChildFragments.add(childFragment); - } - existingFragmentEntity.getChildFragments().clear(); - existingFragmentEntity.getChildFragments().addAll(updatedChildFragments); - } @Override @Transactional @@ -636,6 +518,116 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } + @Override + @Timed(value = "cps.data.persistence.service.datanode.get", + description = "Time taken to get a data node") + public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName, + final String xpath, + final FetchDescendantsOption fetchDescendantsOption) { + final String targetXpath = getNormalizedXpath(xpath); + final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName, + Collections.singletonList(targetXpath), fetchDescendantsOption); + if (dataNodes.isEmpty()) { + throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath); + } + return dataNodes; + } + + @Override + @Timed(value = "cps.data.persistence.service.datanode.batch.get", + description = "Time taken to get data nodes") + public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName, + final Collection<String> xpaths, + final FetchDescendantsOption fetchDescendantsOption) { + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths); + fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, + fragmentEntities); + return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); + } + + private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, + final FetchDescendantsOption fetchDescendantsOption) { + if (fetchDescendantsOption.hasNext()) { + return fragmentEntity.getChildFragments().stream() + .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next())) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) { + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + } + + private List<Long> getAnchorIdsForPagination(final DataspaceEntity dataspaceEntity, final CpsPathQuery cpsPathQuery, + final PaginationOption paginationOption) { + return fragmentRepository.findAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, paginationOption); + } + + private static String getNormalizedXpath(final String xpathSource) { + if (isRootXpath(xpathSource)) { + return xpathSource; + } + try { + return CpsPathUtil.getNormalizedXpath(xpathSource); + } catch (final PathParsingException pathParsingException) { + throw new CpsPathException(pathParsingException.getMessage()); + } + } + + private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) { + final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size()); + for (final String xpath : xpaths) { + try { + normalizedXpaths.add(getNormalizedXpath(xpath)); + } catch (final CpsPathException cpsPathException) { + log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage()); + } + } + return normalizedXpaths; + } + + private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) { + final FragmentEntity fragmentEntity; + if (isRootXpath(xpath)) { + fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null); + } else { + fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath)); + } + if (fragmentEntity == null) { + throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath); + } + return fragmentEntity; + } + + private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity, + final Collection<String> xpaths) { + final Collection<String> normalizedXpaths = getNormalizedXpaths(xpaths); + + final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath); + + final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, + normalizedXpaths); + + for (final FragmentEntity fragmentEntity : fragmentEntities) { + normalizedXpaths.remove(fragmentEntity.getXpath()); + } + + for (final String xpath : normalizedXpaths) { + if (!CpsPathUtil.isPathToListElement(xpath)) { + fragmentEntities.addAll(fragmentRepository.findListByAnchorAndXpath(anchorEntity, xpath)); + } + } + + if (haveRootXpath) { + fragmentEntities.addAll(fragmentRepository.findRootsByAnchorId(anchorEntity.getId())); + } + + return fragmentEntities; + } + private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) { if (newListElements.isEmpty()) { throw new CpsAdminException("Invalid list replacement", @@ -660,20 +652,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return existingListElementEntity; } - private static boolean isNewDataNode(final DataNode replacementDataNode, - final Map<String, FragmentEntity> existingListElementsByXpath) { - return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); - } - - private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity, - final DataNode newDataNode) { - final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes()); - final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves()); - if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) { - existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); - } - } - private String getOrderedLeavesAsJson(final Map<String, Serializable> currentLeaves) { final Map<String, Serializable> sortedLeaves = new TreeMap<>(String::compareTo); sortedLeaves.putAll(currentLeaves); @@ -685,7 +663,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return "{}"; } final Map<String, Serializable> sortedLeaves = jsonObjectMapper.convertJsonString(currentLeavesAsString, - TreeMap.class); + TreeMap.class); return jsonObjectMapper.asJsonString(sortedLeaves); } @@ -696,10 +674,39 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity)); } + private static Set<String> processAncestorXpath(final Collection<FragmentEntity> fragmentEntities, + final CpsPathQuery cpsPathQuery) { + final Set<String> ancestorXpath = new HashSet<>(); + final Pattern pattern = + Pattern.compile("(.*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier()) + + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/.*"); + for (final FragmentEntity fragmentEntity : fragmentEntities) { + final Matcher matcher = pattern.matcher(fragmentEntity.getXpath()); + if (matcher.matches()) { + ancestorXpath.add(matcher.group(1)); + } + } + return ancestorXpath; + } + private static boolean isRootXpath(final String xpath) { return "/".equals(xpath) || "".equals(xpath); } + private static boolean isNewDataNode(final DataNode replacementDataNode, + final Map<String, FragmentEntity> existingListElementsByXpath) { + return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); + } + + private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity, + final DataNode newDataNode) { + final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes()); + final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves()); + if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) { + existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); + } + } + private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) { Map<String, Serializable> currentLeavesAsMap = new HashMap<>(); if (currentLeavesAsString != null) { @@ -712,9 +719,4 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } return jsonObjectMapper.asJsonString(currentLeavesAsMap); } - - private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - } } |