X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=cps-ri%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fcps%2Fspi%2Fimpl%2FCpsDataPersistenceServiceImpl.java;h=fd47793a7a7b6f7a61ac1a984a6b3c1515c5fd3f;hb=HEAD;hp=593ed1334463aee64ba475b40c873dfcc8d0c363;hpb=490834698eef6c8c0ef31c9a50f8695487b7f961;p=cps.git 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 deleted file mode 100644 index 593ed13344..0000000000 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ /dev/null @@ -1,757 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2020-2022 Bell Canada. - * Modifications Copyright (C) 2022-2023 TechMahindra Ltd. - * ================================================================================ - * 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.spi.impl; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet.Builder; -import io.micrometer.core.annotation.Timed; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import javax.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.hibernate.StaleStateException; -import org.onap.cps.cpspath.parser.CpsPathQuery; -import org.onap.cps.cpspath.parser.CpsPathUtil; -import org.onap.cps.cpspath.parser.PathParsingException; -import org.onap.cps.spi.CpsDataPersistenceService; -import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.FragmentEntity; -import org.onap.cps.spi.entities.FragmentEntityArranger; -import org.onap.cps.spi.entities.FragmentExtract; -import org.onap.cps.spi.exceptions.AlreadyDefinedException; -import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch; -import org.onap.cps.spi.exceptions.ConcurrencyException; -import org.onap.cps.spi.exceptions.CpsAdminException; -import org.onap.cps.spi.exceptions.CpsPathException; -import org.onap.cps.spi.exceptions.DataNodeNotFoundException; -import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch; -import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.model.DataNodeBuilder; -import org.onap.cps.spi.repository.AnchorRepository; -import org.onap.cps.spi.repository.DataspaceRepository; -import org.onap.cps.spi.repository.FragmentQueryBuilder; -import org.onap.cps.spi.repository.FragmentRepository; -import org.onap.cps.spi.utils.SessionManager; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService { - - private final DataspaceRepository dataspaceRepository; - private final AnchorRepository anchorRepository; - private final FragmentRepository fragmentRepository; - private final JsonObjectMapper jsonObjectMapper; - private final SessionManager sessionManager; - - private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?])?)"; - - @Override - public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final DataNode newChildDataNode) { - addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode); - } - - @Override - public void addChildDataNodes(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final Collection dataNodes) { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes); - } - - @Override - public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection newListElements) { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements); - } - - @Override - public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection> newLists) { - final Collection failedXpaths = new HashSet<>(); - newLists.forEach(newList -> { - try { - addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList); - } catch (final AlreadyDefinedExceptionBatch e) { - failedXpaths.addAll(e.getAlreadyDefinedXpaths()); - } - }); - - if (!failedXpaths.isEmpty()) { - throw new AlreadyDefinedExceptionBatch(failedXpaths); - } - - } - - private void addNewChildDataNode(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final DataNode newChild) { - final FragmentEntity parentFragmentEntity = - getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); - final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(), - parentFragmentEntity.getAnchor(), newChild); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - try { - fragmentRepository.save(newChildAsFragmentEntity); - } catch (final DataIntegrityViolationException e) { - throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorName, e); - } - - } - - private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection newChildren) { - final FragmentEntity parentFragmentEntity = - getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); - final List fragmentEntities = new ArrayList<>(newChildren.size()); - try { - newChildren.forEach(newChildAsDataNode -> { - final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(), - parentFragmentEntity.getAnchor(), newChildAsDataNode); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - fragmentEntities.add(newChildAsFragmentEntity); - }); - fragmentRepository.saveAll(fragmentEntities); - } catch (final DataIntegrityViolationException e) { - log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", - e, fragmentEntities.size()); - retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren); - } - } - - private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName, - final String parentNodeXpath, - final Collection newChildren) { - final Collection failedXpaths = new HashSet<>(); - for (final DataNode newChild : newChildren) { - try { - addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild); - } catch (final AlreadyDefinedException e) { - failedXpaths.add(newChild.getXpath()); - } - } - if (!failedXpaths.isEmpty()) { - throw new AlreadyDefinedExceptionBatch(failedXpaths); - } - } - - @Override - public void storeDataNode(final String dataspaceName, final String anchorName, final DataNode dataNode) { - storeDataNodes(dataspaceName, anchorName, Collections.singletonList(dataNode)); - } - - @Override - public void storeDataNodes(final String dataspaceName, final String anchorName, - final Collection dataNodes) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - final List fragmentEntities = new ArrayList<>(dataNodes.size()); - try { - for (final DataNode dataNode: dataNodes) { - final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity, - dataNode); - fragmentEntities.add(fragmentEntity); - } - fragmentRepository.saveAll(fragmentEntities); - } catch (final DataIntegrityViolationException exception) { - log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually", - exception, dataNodes.size()); - storeDataNodesIndividually(dataspaceName, anchorName, dataNodes); - } - } - - private void storeDataNodesIndividually(final String dataspaceName, final String anchorName, - final Collection dataNodes) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - final Collection failedXpaths = new HashSet<>(); - for (final DataNode dataNode: dataNodes) { - try { - final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity, - dataNode); - fragmentRepository.save(fragmentEntity); - } catch (final DataIntegrityViolationException e) { - failedXpaths.add(dataNode.getXpath()); - } - } - if (!failedXpaths.isEmpty()) { - throw new AlreadyDefinedExceptionBatch(failedXpaths); - } - } - - /** - * Convert DataNode object into Fragment and places the result in the fragments placeholder. Performs same action - * for all DataNode children recursively. - * - * @param dataspaceEntity dataspace - * @param anchorEntity anchorEntity - * @param dataNodeToBeConverted dataNode - * @return a Fragment built from current DataNode - */ - private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, - final AnchorEntity anchorEntity, - final DataNode dataNodeToBeConverted) { - final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted); - final Builder childFragmentsImmutableSetBuilder = ImmutableSet.builder(); - for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) { - final FragmentEntity childFragment = - convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(), - childDataNode); - childFragmentsImmutableSetBuilder.add(childFragment); - } - parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build()); - return parentFragment; - } - - private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, - final AnchorEntity anchorEntity, final DataNode dataNode) { - return FragmentEntity.builder() - .dataspace(dataspaceEntity) - .anchor(anchorEntity) - .xpath(dataNode.getXpath()) - .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) - .build(); - } - - @Override - @Timed(value = "cps.data.persistence.service.datanode.get", - description = "Time taken to get a data node") - public Collection getDataNodes(final String dataspaceName, final String anchorName, - final String xpath, - final FetchDescendantsOption fetchDescendantsOption) { - final String targetXpath = getNormalizedXpath(xpath); - final Collection 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 getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName, - final Collection xpaths, - final FetchDescendantsOption fetchDescendantsOption) { - final Collection fragmentEntities = getFragmentEntities(dataspaceName, anchorName, xpaths); - return toDataNodes(fragmentEntities, fetchDescendantsOption); - } - - private Collection getFragmentEntities(final String dataspaceName, final String anchorName, - final Collection xpaths) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - - final Collection nonRootXpaths = new HashSet<>(xpaths); - final boolean haveRootXpath = nonRootXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath); - - final Collection normalizedXpaths = new HashSet<>(nonRootXpaths.size()); - for (final String xpath : nonRootXpaths) { - try { - normalizedXpaths.add(CpsPathUtil.getNormalizedXpath(xpath)); - } catch (final PathParsingException e) { - log.warn("Error parsing xpath \"{}\": {}", xpath, e.getMessage()); - } - } - final Collection fragmentEntities = - new HashSet<>(fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths)); - - if (haveRootXpath) { - final List fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity, - anchorEntity); - fragmentEntities.addAll(FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts)); - } - - return fragmentEntities; - } - - private FragmentEntity getFragmentEntity(final String dataspaceName, final String anchorName, final String xpath) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - final FragmentEntity fragmentEntity; - if (isRootXpath(xpath)) { - final List fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity, - anchorEntity); - fragmentEntity = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts) - .stream().findFirst().orElse(null); - } else { - final String normalizedXpath = getNormalizedXpath(xpath); - fragmentEntity = - fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath); - } - if (fragmentEntity == null) { - throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath); - } - return fragmentEntity; - - } - - private Collection buildFragmentEntitiesFromFragmentExtracts(final AnchorEntity anchorEntity, - final String normalizedXpath) { - final List fragmentExtracts = - fragmentRepository.findByAnchorIdAndParentXpath(anchorEntity.getId(), normalizedXpath); - log.debug("Fetched {} fragment entities by anchor {} and cps path {}.", - fragmentExtracts.size(), anchorEntity.getName(), normalizedXpath); - return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); - - } - - @Override - @Timed(value = "cps.data.persistence.service.datanode.query", - description = "Time taken to query data nodes") - public List queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - final CpsPathQuery cpsPathQuery; - try { - cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); - } catch (final PathParsingException e) { - throw new CpsPathException(e.getMessage()); - } - - Collection fragmentEntities; - if (canUseRegexQuickFind(fetchDescendantsOption, cpsPathQuery)) { - return getDataNodesUsingRegexQuickFind(fetchDescendantsOption, anchorEntity, cpsPathQuery); - } - fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery); - if (cpsPathQuery.hasAncestorAxis()) { - fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities); - } - return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities); - } - - private static boolean canUseRegexQuickFind(final FetchDescendantsOption fetchDescendantsOption, - final CpsPathQuery cpsPathQuery) { - return fetchDescendantsOption.equals(FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) - && !cpsPathQuery.hasLeafConditions() - && !cpsPathQuery.hasTextFunctionCondition(); - } - - private List getDataNodesUsingRegexQuickFind(final FetchDescendantsOption fetchDescendantsOption, - final AnchorEntity anchorEntity, - final CpsPathQuery cpsPathQuery) { - Collection fragmentEntities; - final String xpathRegex = FragmentQueryBuilder.getXpathSqlRegex(cpsPathQuery, true); - final List fragmentExtracts = - fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex); - fragmentEntities = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); - if (cpsPathQuery.hasAncestorAxis()) { - fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities); - } - return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); - } - - private Collection getAncestorFragmentEntities(final int anchorId, - final CpsPathQuery cpsPathQuery, - final Collection fragmentEntities) { - final Collection ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - return ancestorXpaths.isEmpty() ? Collections.emptyList() - : fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorId, ancestorXpaths); - } - - private List createDataNodesFromProxiedFragmentEntities( - final FetchDescendantsOption fetchDescendantsOption, - final AnchorEntity anchorEntity, - final Collection proxiedFragmentEntities) { - final List dataNodes = new ArrayList<>(proxiedFragmentEntities.size()); - for (final FragmentEntity proxiedFragmentEntity : proxiedFragmentEntities) { - if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) { - dataNodes.add(toDataNode(proxiedFragmentEntity, fetchDescendantsOption)); - } else { - final String normalizedXpath = getNormalizedXpath(proxiedFragmentEntity.getXpath()); - final Collection unproxiedFragmentEntities = - buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath); - for (final FragmentEntity unproxiedFragmentEntity : unproxiedFragmentEntities) { - dataNodes.add(toDataNode(unproxiedFragmentEntity, fetchDescendantsOption)); - } - } - } - return Collections.unmodifiableList(dataNodes); - } - - private List createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption, - final Collection fragmentEntities) { - final List dataNodes = new ArrayList<>(fragmentEntities.size()); - for (final FragmentEntity fragmentEntity : fragmentEntities) { - dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption)); - } - return Collections.unmodifiableList(dataNodes); - } - - private static String getNormalizedXpath(final String xpathSource) { - if (isRootXpath(xpathSource)) { - return xpathSource; - } - try { - return CpsPathUtil.getNormalizedXpath(xpathSource); - } catch (final PathParsingException e) { - throw new CpsPathException(e.getMessage()); - } - } - - @Override - public String startSession() { - return sessionManager.startSession(); - } - - @Override - public void closeSession(final String sessionId) { - sessionManager.closeSession(sessionId, SessionManager.WITH_COMMIT); - } - - @Override - public void lockAnchor(final String sessionId, final String dataspaceName, - final String anchorName, final Long timeoutInMilliseconds) { - sessionManager.lockAnchor(sessionId, dataspaceName, anchorName, timeoutInMilliseconds); - } - - private static Set processAncestorXpath(final Collection fragmentEntities, - final CpsPathQuery cpsPathQuery) { - final Set ancestorXpath = new HashSet<>(); - final Pattern pattern = - Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier()) - + REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*"); - 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 childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); - Map leaves = new HashMap<>(); - if (fragmentEntity.getAttributes() != null) { - leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); - } - return new DataNodeBuilder() - .withXpath(fragmentEntity.getXpath()) - .withLeaves(leaves) - .withChildDataNodes(childDataNodes).build(); - } - - private Collection toDataNodes(final Collection fragmentEntities, - final FetchDescendantsOption fetchDescendantsOption) { - final Collection dataNodes = new ArrayList<>(fragmentEntities.size()); - for (final FragmentEntity fragmentEntity : fragmentEntities) { - dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption)); - } - return dataNodes; - } - - private List 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 updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath, - final Map updateLeaves) { - final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, xpath); - final String currentLeavesAsString = fragmentEntity.getAttributes(); - final String mergedLeaves = mergeLeaves(updateLeaves, currentLeavesAsString); - fragmentEntity.setAttributes(mergedLeaves); - fragmentRepository.save(fragmentEntity); - } - - @Override - public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, - final DataNode dataNode) { - final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, dataNode.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode); - try { - fragmentRepository.save(fragmentEntity); - } catch (final StaleStateException staleStateException) { - throw new ConcurrencyException("Concurrent Transactions", - String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.", - dataspaceName, anchorName, dataNode.getXpath())); - } - } - - @Override - public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, - final List updatedDataNodes) { - final Map xpathToUpdatedDataNode = updatedDataNodes.stream() - .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); - - final Collection xpaths = xpathToUpdatedDataNode.keySet(); - final Collection existingFragmentEntities = - getFragmentEntities(dataspaceName, anchorName, xpaths); - - for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) { - final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode); - } - - try { - fragmentRepository.saveAll(existingFragmentEntities); - } catch (final StaleStateException staleStateException) { - retryUpdateDataNodesIndividually(dataspaceName, anchorName, existingFragmentEntities); - } - } - - private void retryUpdateDataNodesIndividually(final String dataspaceName, final String anchorName, - final Collection fragmentEntities) { - final Collection failedXpaths = new HashSet<>(); - - fragmentEntities.forEach(dataNodeFragment -> { - try { - fragmentRepository.save(dataNodeFragment); - } catch (final StaleStateException e) { - 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, dataspaceName, anchorName)); - } - } - - private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity, - final DataNode newDataNode) { - - existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); - - final Map existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream() - .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); - - final Collection updatedChildFragments = new HashSet<>(); - - for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) { - final FragmentEntity childFragment; - if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) { - childFragment = convertToFragmentWithAllDescendants( - existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild); - } else { - childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild); - } - updatedChildFragments.add(childFragment); - } - - existingFragmentEntity.getChildFragments().clear(); - existingFragmentEntity.getChildFragments().addAll(updatedChildFragments); - } - - @Override - @Transactional - public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final Collection newListElements) { - final FragmentEntity parentEntity = getFragmentEntity(dataspaceName, anchorName, parentNodeXpath); - final String listElementXpathPrefix = getListElementXpathPrefix(newListElements); - final Map existingListElementFragmentEntitiesByXPath = - extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix); - parentEntity.getChildFragments().removeAll(existingListElementFragmentEntitiesByXPath.values()); - final Set updatedChildFragmentEntities = new HashSet<>(); - for (final DataNode newListElement : newListElements) { - final FragmentEntity existingListElementEntity = - existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath()); - final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, newListElement, - existingListElementEntity); - - updatedChildFragmentEntities.add(entityToBeAdded); - } - parentEntity.getChildFragments().addAll(updatedChildFragmentEntities); - fragmentRepository.save(parentEntity); - } - - @Override - @Transactional - public void deleteDataNodes(final String dataspaceName, final String anchorName) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName) - .ifPresent(anchorEntity -> fragmentRepository.deleteByAnchorIn(Collections.singletonList(anchorEntity))); - } - - @Override - @Transactional - public void deleteDataNodes(final String dataspaceName, final Collection anchorNames) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final Collection anchorEntities = - anchorRepository.findAllByDataspaceAndNameIn(dataspaceEntity, anchorNames); - fragmentRepository.deleteByAnchorIn(anchorEntities); - } - - @Override - @Transactional - public void deleteDataNodes(final String dataspaceName, final String anchorName, - final Collection xpathsToDelete) { - deleteDataNodes(dataspaceName, anchorName, xpathsToDelete, false); - } - - private void deleteDataNodes(final String dataspaceName, final String anchorName, - final Collection xpathsToDelete, final boolean onlySupportListDeletion) { - final boolean haveRootXpath = xpathsToDelete.stream().anyMatch(CpsDataPersistenceServiceImpl::isRootXpath); - if (haveRootXpath) { - deleteDataNodes(dataspaceName, anchorName); - return; - } - - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - - final Collection deleteChecklist = new HashSet<>(xpathsToDelete.size()); - for (final String xpath : xpathsToDelete) { - try { - deleteChecklist.add(CpsPathUtil.getNormalizedXpath(xpath)); - } catch (final PathParsingException e) { - log.debug("Error parsing xpath \"{}\": {}", xpath, e.getMessage()); - } - } - - final Collection xpathsToExistingContainers = - fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist); - if (onlySupportListDeletion) { - final Collection xpathsToExistingListElements = xpathsToExistingContainers.stream() - .filter(CpsPathUtil::isPathToListElement).collect(Collectors.toList()); - deleteChecklist.removeAll(xpathsToExistingListElements); - } else { - deleteChecklist.removeAll(xpathsToExistingContainers); - } - - final Collection xpathsToExistingLists = deleteChecklist.stream() - .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "[")) - .collect(Collectors.toList()); - deleteChecklist.removeAll(xpathsToExistingLists); - - if (!deleteChecklist.isEmpty()) { - throw new DataNodeNotFoundExceptionBatch(dataspaceName, anchorName, deleteChecklist); - } - - fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers); - fragmentRepository.deleteListsByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingLists); - } - - @Override - @Transactional - public void deleteListDataNode(final String dataspaceName, final String anchorName, - final String targetXpath) { - deleteDataNode(dataspaceName, anchorName, targetXpath, true); - } - - @Override - @Transactional - public void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath) { - deleteDataNode(dataspaceName, anchorName, targetXpath, false); - } - - private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath, - final boolean onlySupportListNodeDeletion) { - final String normalizedXpath = getNormalizedXpath(targetXpath); - try { - deleteDataNodes(dataspaceName, anchorName, Collections.singletonList(normalizedXpath), - onlySupportListNodeDeletion); - } catch (final DataNodeNotFoundExceptionBatch dataNodeNotFoundExceptionBatch) { - throw new DataNodeNotFoundException(dataspaceName, anchorName, targetXpath); - } - } - - private static String getListElementXpathPrefix(final Collection newListElements) { - if (newListElements.isEmpty()) { - throw new CpsAdminException("Invalid list replacement", - "Cannot replace list elements with empty collection"); - } - final String firstChildNodeXpath = newListElements.iterator().next().getXpath(); - return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf('[') + 1); - } - - private FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, - final DataNode newListElement, - final FragmentEntity existingListElementEntity) { - if (existingListElementEntity == null) { - return convertToFragmentWithAllDescendants( - parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement); - } - if (newListElement.getChildDataNodes().isEmpty()) { - copyAttributesFromNewListElement(existingListElementEntity, newListElement); - existingListElementEntity.getChildFragments().clear(); - } else { - updateFragmentEntityAndDescendantsWithDataNode(existingListElementEntity, newListElement); - } - return existingListElementEntity; - } - - private static boolean isNewDataNode(final DataNode replacementDataNode, - final Map existingListElementsByXpath) { - return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); - } - - private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity, - final DataNode newListElement) { - final FragmentEntity replacementFragmentEntity = - FragmentEntity.builder().attributes(jsonObjectMapper.asJsonString( - newListElement.getLeaves())).build(); - existingListElementEntity.setAttributes(replacementFragmentEntity.getAttributes()); - } - - private static Map extractListElementFragmentEntitiesByXPath( - final Set childEntities, final String listElementXpathPrefix) { - return childEntities.stream() - .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listElementXpathPrefix)) - .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity)); - } - - private static boolean isRootXpath(final String xpath) { - return "/".equals(xpath) || "".equals(xpath); - } - - private String mergeLeaves(final Map updateLeaves, final String currentLeavesAsString) { - final Map currentLeavesAsMap = currentLeavesAsString.isEmpty() - ? new HashMap<>() : jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class); - currentLeavesAsMap.putAll(updateLeaves); - if (currentLeavesAsMap.isEmpty()) { - return ""; - } - return jsonObjectMapper.asJsonString(currentLeavesAsMap); - } -}